自学内容网 自学内容网

Hooks:尽享React特性 ,重塑开发体验

🎼 React 16.8 版本引入了 Hooks ,可以在不使用 Class 的情况下使用 React 特性。

Hooks 允许从函数组件 “hook into” React 状态和生命周期特性。

function Counter () {
  const [count, setCount] = useState(0);
  
  return (<>
  <p>{count}</p>
    <button onClick={() => setCount(count + 1)}> +1 </button>
  </>)
}

为什么引入 Hooks ?

一个新的方案引入,一定是为了解决现存的问题。对于 Hooks 来说,就是为解决 Class 的诟病。

  • 组件之间复用状态逻辑异常困难,存在“回调地狱”的风险 ==> render props 和 Hoc 高阶组件都需要重新构造组件。
  • 复杂的组件难以理解及维护(状态逻辑及副作用堆积)==> 常见的,每个生命周期方法中包含了一组不相关的逻辑。
  • 基于 Class 的组件,比较难以理解,且不能很好的控制范围,对于热更新不友好,优化路径不佳。

因此,引入了 Hooks:

  • 使用 Hooks 可以从组件中提取有状态逻辑,这样就可以独立地对其进行测试并复用。其允许在不改变组件层次结构的情况下复用有状态逻辑。这样可以很容易在许多组件之间或与社区共享 Hook。
  • 使用 Hooks 可以将一个组件拆分为更小的函数,而不是强制基于生命周期方法进行拆分。也可以选择使用 reduce 来管理组件的本地状态,以使其更可预测。
  • Hooks 允许在不使用类的情况下更多地使用 React 的特性。从概念上讲,React 组件总是更接近于函数,不需要学习复杂的功能或响应式编程技术。

Hooks 是否可以完全取代 render props 和 Hoc 组件?1

答:不能,例如虚拟滚动组件需要具有 renderItem prop,以及可视化容器组件可能具有自己的DOM结构。

✔️ Hooks 让我们根据代码所做的,而不是生命周期方法名称来分割代码。React 组件一直更像是函数,而 Hooks 则拥抱了函数。

Hooks 使用规则(调用位置有限制)

✅ 在函数组件的顶层调用 Hooks
✅ 在 React 的函数组件或自定义Hooks中调用 Hook

下述以 useState(React 内置钩子) 为例:

// ✅ 在函数组件的顶层调用
function Counter () {
  const [count, setCount] = useState(0);
}

// ✅ 在自定义Hooks的顶层调用
function useWindowWidth () {
  const [width, setWidth] = useState(window.innerWidth);
}

不要在循环、条件、嵌套函数 或 try/catch/finally 块中调用。这样可以做到各个 Hook 在每一次渲染中,调用的顺序是一致的。

const [count, setCount] = useState(0);

数组结构语法允许我们为状态变量赋予不同的名称。这些名称不是 useState API 的一部分。

替代生命周期

  • constructor: 函数组件不需要 constructor,可以通过 useState 初始化(如果数据复杂,可以传入函数);
  • getDerivedStateFromProps:渲染过程更新
  • shouldComponentUpdate:使用 React.memo
  • componentDidMount, componentDidUpdate, componentWillUnmount:Effect Hooks 可以替代

示例:Class 形式:

class FriendStatusWithCounter extends React.Component {
  constructor(props) {
    super(props);
  }
  
  // 初始化:订阅
  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(...);
  }

  // DOM更新:先取消再重新订阅
  componentDidUpdate(prevProps) {
    ChatAPI.unsubscribeFromFriendStatus(...);
    ChatAPI.subscribeToFriendStatus(...);
  }

  // 卸载:取消订阅
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(...);
  }
}

Hooks 形式:

function FriendStatus(props) {
  useEffect(() => {
    // 订阅
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      // 取消
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    }
})
}

如何获取 previous props 或 state ?

useEffect(() => {
  ChatAPI.subscribeToFriendStatus(props.friend.id);
  return () => ChatAPI.unsubscribeFromFriendStatus(props.friend.id);
}, [props.userId]);

在上面的示例中,如果 Id 从 3 => 4,ChatAPI.unsubscribeFromFriendStatus(3) 将首先运行,然后 ChatAPI.subscribeToFriendStatus(4) 将运行。不需要获取 “previous Id”,因为 clean up 函数将在闭包中捕获它。

➰或者可以将以前的 state 或 props 存储。

function ScrollView({row}) {
  const [isScrollingDown, setIsScrollingDown] = useState(false);
  // 存储之前值
  const [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) {
    // 自上次渲染后更改
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }

  return `Scrolling down: ${isScrollingDown}`;
}

内置 Hooks

State Hook2

可以用于记住用户输入的信息。

Context Hook

从祖先组件接收信息,而无需将其作为 props 传递。

// ① 创建context
const ThemeContext = createContext(null);

export default () => {
    const [color, setColor] = useState('red');
    return (<>
        {/* ② 使用 context provider 进行包裹 */}    
        <ThemeContext.Provider value={color}>
            <MyText />
        </ThemeContext.Provider>
    </>)
}

function MyText() {
  // ③ 使用
    const color = useContext(ThemeContext);
    return (<>
        <div>{color}</div>
    </>)
}
Ref Hook 3

保存一些不用于渲染的信息,比如 DOM 节点或 timeout ID。

  • 使用 useRef 声明 ref。你可以在其中保存任何值,但最常用于保存 DOM 节点。
  • 使用 useImperativeHandle 自定义从组件中暴露的 ref,但是很少使用。
Effect Hook 4

连接到外部系统并与之同步。这包括处理网络、浏览器、DOM、动画、使用不同 UI 库编写的小部件以及其他非 React 代码。

  • 使用 useEffect 将组件连接到外部系统。
  • useLayoutEffect 在浏览器重新绘制屏幕前执行,可以在此处测量布局。
  • useInsertionEffect 在 React 对 DOM 进行更改之前触发,库可以在此处插入动态 CSS。
性能 Hook

优化重新渲染性能的一种常见方法是跳过不必要的工作。例如,可以告诉 React 重用缓存的计算结果,或者如果数据自上次渲染以来没有更改,则跳过重新渲染:

  • 使用 useMemo 缓存计算代价昂贵的计算结果。
  • 使用 useCallback 将函数传递给优化组件之前缓存函数定义。

将必须同步的阻塞更新(比如使用输入法输入内容)与不需要阻塞用户界面的非阻塞更新(比如更新图表)分离以提高性能:

  • useTransition 允许将状态转换标记为非阻塞,并允许其他更新中断它。
  • useDeferredValue 允许延迟更新 UI 的非关键部分,以让其他部分先更新。
其他 Hook
  • 使用 useDebugValue 自定义 React 开发者工具为自定义 Hook 添加的标签。
  • 使用 useId 将唯一的 ID 与组件相关联,其通常与可访问性 API 一起使用。
  • 使用 useSyncExternalStore 订阅外部 store。

  1. https://legacy.reactjs.org/docs/hooks-faq.html#which-versions-of-react-include-hooks react hook FAQ ↩︎

  2. https://blog.csdn.net/ligang2585116/article/details/136458885 总结:React 中的 state 状态 ↩︎

  3. https://blog.csdn.net/ligang2585116/article/details/136626405 脱围:使用 ref 保存值及操作DOM ↩︎

  4. https://blog.csdn.net/ligang2585116/article/details/136880009?spm=1001.2014.3001.5501 Effect:由渲染本身引起的副作用 ↩︎


原文地址:https://blog.csdn.net/ligang2585116/article/details/137631509

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!