创建

  1. function App() { // 普通函数
  2. return (<div>1</div>);
  3. }
  4. const App = () => { // 箭头函数
  5. return (<div>1</div>)
  6. }
  7. const App = () => (<div>1</div>) // 简写

生命周期:Effect Hook

  • Effect Hook:副作用钩子。

    • 副作用:改变了当前环境,数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。

      useEffect 做了什么?

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

    为什么在组件内部调用 useEffect

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

    useEffect 对应类组件中的生命周期

    ```javascript useEffect( () => {console.log(1)},[] ) ===== componentDidMount // 只在挂载时触发

useEffect( () => {console.log(1)},[n] ) ===== componentDidUpdate
// 第一次也会执行,只在 n 更新时更新,不写表示监听 state props

useEffect( () => { ===== componentWillUnmount // 组件消亡时触发 console.log(1) return () => {} } )

  1. <a name="R4pc0"></a>
  2. # 性能优化
  3. **[stale closure(旧的闭包) ](https://dmitripavlutin.com/react-hooks-stale-closures/)- 在 setInterval 中使用 setState 时,因为闭包产生的 bug**
  4. <a name="sYMzL"></a>
  5. ### 如果 effect 的依赖频繁变化,该怎么办?
  6. - 有时候,你的 effect 可能会使用一些频繁变化的值。你可能会忽略依赖列表中 state,但这通常会引起 Bug:
  7. ```javascript
  8. function Counter() {
  9. const [count, setCount] = useState(0);
  10. useEffect(() => {
  11. const id = setInterval(() => {
  12. setCount(count + 1); // 这个 effect 依赖于 `count` state }, 1000);
  13. return () => clearInterval(id);
  14. }, []); // 🔴 Bug: `count` 没有被指定为依赖
  15. return <h1>{count}</h1>;
  16. }
  • 传入空的依赖数组 [],意味着该 hook 只在组件挂载时运行一次,并非重新渲染时。但如此会有问题,在 setInterval 的回调中,count 的值不会发生变化。因为当 effect 执行时,我们会创建一个闭包,并将 count 的值被保存在该闭包当中,且初值为 0。每隔一秒,回调就会执行 setCount(0 + 1),因此,count 永远不会超过 1。
  • 指定 [count] 作为依赖列表就能修复这个 Bug,但会导致每次改变发生时定时器都被重置。事实上,每个 setInterval 在被清除前(类似于 setTimeout)都会调用一次。但这并不是我们想要的。要解决这个问题,我们可以使用 setState 的函数式更新形式。它允许我们指定 state 该 如何 改变而不用引用state。

    1. function Counter() {
    2. const [count, setCount] = useState(0);
    3. useEffect(() => {
    4. const id = setInterval(() => {
    5. setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量 }, 1000);
    6. return () => clearInterval(id);
    7. }, []); // ✅ 我们的 effect 不使用组件作用域中的任何变量
    8. return <h1>{count}</h1>;
    9. }

    setCount 函数的身份是被确保稳定的,所以可以放心的省略掉)
    此时,setInterval 的回调依旧每秒调用一次,但每次 setCount 内部的回调取到的 count 是最新值(在回调中变量命名为 c)。

    自定义 Hook

  • 挂载时不执行,只在数据更新时执行

    1. function useUpdate(fn, array) {
    2. const [count, setCount] = useState(0)
    3. useEffect(() => {
    4. setCount(x => x + 1)
    5. }, array)
    6. useEffect(() => {
    7. if (count > 1) {
    8. fn()
    9. }
    10. }, [count])
    11. }