React学习笔记之Hooks

1. useState

state 只在组件首次渲染的时候被创建

useState Hook: 允许我们在函数组件中存储内部 state。

const [fruit, setFruit] = useState('banana'); //结构语法

2. useEffect

useEffect Hook 可看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

 React 组件中有两种常见副作用操作:需要清除的和不需要清除的。我们来更仔细地看一下他们之间的区别。

when?

euseEffect允许在组件加载和更新时执行操作。从概念上说,我们希望有些操作在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。如把副作用操作放到 componentDidMount  和 componentDidUpdate 函数中

// 在class组件中
componentDidMount() {     //首次渲染时
    document.title = `You clicked ${this.sta执行te.count} times`;  
}  
componentDidUpdate() {    //更新时
    document.title = `You clicked ${this.state.count} times`;  
}

// 用useEffect
useEffect(()=>{
    document.title = `You clicked ${count} times`;  
)

Do what?

Effect可以在React 组件渲染后执行某些操作。当 React 渲染组件时,React 会保存你传递的函数(我们将它称之为 “effect”),并在更新完 DOM 后执行它。也就是说它在渲染之后和每次更新之后都会执行。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。我们可以在 effect 中获取到最新的 变量值,变量值需要在函数的作用域内。这个过程在每次渲染时都会发生,包括首次渲染。

ps:传递给 useEffect 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 count 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。

在class组件中

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate(prevProps) {
    // 取消订阅之前的 friend.id
    ChatAPI.unsubscribeFromFriendStatus(
      prevProps.friend.id,
      this.handleStatusChange
    );
    // 订阅新的 friend.id
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

 但是用useEffect情况下

// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // 运行第一个 effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // 运行下一个 effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // 运行下一个 effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一个 effect

每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。 React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行当前 effect 之前对上一个 effect 进行清除。

对于多个effect声明,React 将按照 effect 声明的顺序依次调用组件中的每一个 effect。

忘记正确地理 componentDidUpdate 是 React 应用中常见的 bug 来源,但是useEffect 默认就会处理。它会在调用一个新的 effect 之前对前一个 effect 进行清理。此默认行为保证了一致性,避免了在 class 组件中因为没有处理更新逻辑而导致常见的 bug。

原理

Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。将 useEffect 放在组件内部让我们可以在 effect 中直接访问 count state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。

Effect进行性能优化

因为effect 在每次渲染后都执行清理或者执行 effect 可能会导致性能问题,在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决:

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    document.title = `You clicked ${this.state.count} times`;
  }
}

effect在修改时更新的方法,如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可,如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。想使用性能优化需要确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
  1. Hooks规则
    1.只在最顶层使用 Hook:不要在循环,条件或嵌套函数中调用 Hook,如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的内部。

2.只在React函数中调用Hook

  1. 自定义Hooks
    共享组件之间的状态逻辑:流行的有: render props和高阶组件,Hooks可以让你在不增加组件的情况下解决相同问题

如何实现reducer的状态管理:编写一个 useReducer 的 Hook,使用 reducer 的方式来管理组件的内部 state 

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}
//在组件中使用
function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  function handleAddClick(text) {
    dispatch({ type: 'add', text });
  }

  // ...
  1. 思考
    1.渲染过程

2. React 怎么知道 useState 对应的是哪个组件

3.传递给 useEffect 的函数在每次渲染中都会有所不同

4.组件更新的时候执行清除操作吗?卸载和清除的区别

你可能感兴趣的