React hooks 学习笔记 - useEffect

概念理解:什么是Effect

Pure function(纯函数)

  • 给一个 function 相同的参数,永远会返回相同的值,并且没有副作用;这个概念拿到 React 中,就是给一个 Pure component 相同的 props, 永远渲染出相同的视图,并且没有其他的副作用;纯组件的好处是,容易监测数据变化、容易测试、提高渲染性能等;
  • 副作用(Side Effect)是指一个 function 做了和本身运算返回值无关的事,比如:修改了全局变量、修改了传入的参数、甚至是 console.log(),和ajax 操作,修改 dom 都是算作副作用的;

Effect(副作用)
对于数据抓取,注册监听事件,修改 DOM 元素,发ajax请求…的操作都属于副作用,因为我们渲染出来的页面是静态的,任何在之后的操作都会对它产生影响,所以才称之为副作用。而 useEffect 则是专门用来编写副作用代码的,这也是 React 的核心所在。

使用useEffect,可以直接在函数组件内处理生命周期事件。 根据React class 的生命周期函数,可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

componentDidMount:
在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
(相当于vue的mounted )

componentDidUpdate:
在组件完成更新后立即调用。在初始化时不会被调用。
(大致相当于vue的beforeUpdate )

componentWillUnmount:
在组件从 DOM 中移除之前立刻被调用。
(相当于vue的beforeDestroy)

useEffect 相关特性

useEffect 做了什么?
通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。

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

useEffect 会在每次渲染后都执行吗?
是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

如果我们需要在每次执行后清除副作用(防止内存泄露)
在 effect 中返回一个函数: 这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。
React 何时清除 effect:React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。
例如:

useEffect(() => {    
function handleStatusChange(status) {      
setIsOnline(status.isOnline);    
}    
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    
return function cleanup() {      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);    
}; 
 });

通过跳过 Effect 进行性能优化(有点vue计算属性的感觉)
在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决:

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

这是很常见的需求,所以它被内置到了 useEffect 的 Hook API 中。如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

useEffect的第二个参数可选,如果用上的话,这个参数必须是一个数组

上面这个示例中,我们传入 [count] 作为第二个参数。这个参数是什么作用呢?如果 count 的值是 5,而且我们的组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。
当渲染时,如果 count 的值更新成了 6,React 将会把前一次渲染时的数组 [5] 和这次渲染的数组 [6] 中的元素进行对比。这次因为 5 !== 6,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

踩坑点

1.当第二个参数为[]时,useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值

 const [count, setCount] = useState(0)
    useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => {
            console.log('timer...count:', count)
            setCount(count + 1)
        }, 1000)
        return ()=> clearInterval(timer)
    },[])
  • React Hooks只能用于函数组件,而每一次函数组件被渲染,都是一个全新的开始;
  • 每一个全新的开始,所有的局部变量全都重来,全体失忆;
  • 每一次全新的开始,只有Hooks函数(比如useEffect)具有上一次渲染的“记忆”;
    对于上面说的问题,因为count每次渲染都会改变,而且我们想要useEffect总会用上count的值,所以,就要把count放在useEffect的第二个数组参数里面。
    规矩就是:如果useEffect第一个函数参数直接或者间接用上某个变量,就请把这个变量放在useEffect的第二个参数里。
    2.useEffect不能被判断包裹
const [count, setCount] = useState(0)
if(2 < 5){
   useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => setCount(count +1), 1000)
        return ()=> clearInterval(timer)
    }) 
}

如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的内部:

  useEffect(function persistForm() {
    //  将条件判断放置在 effect 中
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

3.useEffect不能被打断

const [count, setCount] = useState(0)
useEffect(...)

return // 函数提前结束了

useEffect(...)
}

你可能感兴趣的:(react,vue,js)