如何在 React 中使用带有钩子的生命周期方法?

Posted

技术标签:

【中文标题】如何在 React 中使用带有钩子的生命周期方法?【英文标题】:How to use lifecycle methods with hooks in React? 【发布时间】:2019-04-12 08:36:19 【问题描述】:

我已经了解了 react v16.7.0 中引入的钩子。

https://reactjs.org/docs/hooks-intro.html

所以我对钩子的理解是我们可以在函数组件中使用状态,而无需在 react 中编写类组件。这真是一个了不起的功能。

但是我对在功能组件中使用钩子没有清楚的了解。

   import  useState  from 'react';

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

    return (
      <div>
        <p>You clicked count times</p>
        <button onClick=() => setCount(count + 1)>
         Click me
        </button>
      </div>
   );
  

如果我使用钩子,如何在上述功能组件中使用生命周期方法?

【问题讨论】:

reactjs.org/docs/… useState() 本身不是生命周期方法。它只是创建一个运行状态。生命周期方法有条件地被调用,不像你对 useState 的调用。很棒的话题。 这能回答你的问题吗? React Hooks and Component Lifecycle Equivalent 【参考方案1】:

以下是最常见生命周期的示例:

componentDidMount

将一个空数组作为第二个参数传递给useEffect(),以仅在挂载时运行回调。

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

  useEffect(() => 
    document.title = `You clicked $count times`;
  , []); // Pass an empty array to run only callback on mount only.

  return (
    <div>
      <p>You clicked count times</p>
      <button onClick=() => setCount(count + 1)>
        Click me
      </button>
    </div>
  );

componentDidUpdate(松散)

通过仅将单个参数传递给useEffect,它将在每次渲染后运行。这是一个松散的等价物,因为这里略有不同,componentDidUpdate 在第一次渲染后不会运行,但这个钩子版本会在每次渲染后运行。

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

  useEffect(() => 
    document.title = `You clicked $count times`;
  ); // No second argument, so run after every render.

  return (
    <div>
      <p>You clicked count times</p>
      <button onClick=() => setCount(count + 1)>
        Click me
      </button>
    </div>
  );

componentDidUpdate(严格)

这个例子与上面例子的不同之处在于这里的回调不会在初始渲染时运行,严格模仿componentDidUpdate的语义。这个answer is by Tholle,所有功劳归于他。

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

  const firstUpdate = useRef(true);
  useLayoutEffect(() => 
    if (firstUpdate.current) 
      firstUpdate.current = false;
      return;
    

    console.log('componentDidUpdate');
  );

  return (
    <div>
      <p>componentDidUpdate: count times</p>
      <button
        onClick=() => 
          setCount(count + 1);
        
      >
        Click Me
      </button>
    </div>
  );

componentWillUnmount

useEffect的回调参数中返回一个回调,它会在卸载前被调用。

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

  useEffect(() => 
    // Return a callback in useEffect and it will be called before unmounting.
    return () => 
      console.log('componentWillUnmount!');
    ;
  , []);

  return (
    <div>
      <p>You clicked count times</p>
      <button onClick=() => setCount(count + 1)>
        Click me
      </button>
    </div>
  );

shouldComponentUpdate

您已经可以使用React.PureComponentReact.memo 在组件级别实现此目的。为了防止子组件重新渲染,这个例子取自React docs:

function Parent( a, b ) 
  // Only re-rendered if `a` changes:
  const child1 = useMemo(() => <Child1 a=a />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = useMemo(() => <Child2 b=b />, [b]);
  return (
    <>
      child1
      child2
    </>
  )

getDerivedStateFromProps

同样,取自React docs

function ScrollView(row) 
  let [isScrollingDown, setIsScrollingDown] = useState(false);
  let [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) 
    // Row changed since last render. Update isScrollingDown.
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  

  return `Scrolling down: $isScrollingDown`;

getSnapshotBeforeUpdate

还没有钩子的等效项。

componentDidCatch

还没有钩子的等效项。

【讨论】:

这是干净而完美的答案。谢谢你:) 什么是componentWillMount? componentWillUnmount 示例中的 useEffect 回调将在每次重新渲染之前被调用,而不仅仅是在卸载组件时。正确的实现将使用useRef[] deps 数组,例如:const onUnmountRef = useRef(() =&gt; console.log("componentWillUnmount!")); useEffect(() =&gt; onUnmountRef.current, []); 好收获。更新 useLayoutEffectgetSnapshotBeforeUpdate 的近似替代品。使用useRef,您只能让它在更新时触发,而不是在挂载时触发。【参考方案2】:

好吧,您实际上并没有生命周期方法。 =) 但是你可以使用这里显示的效果钩子https://reactjs.org/docs/hooks-effect.html

效果挂钩将能够复制 componentDidMount、componentDidUpdate 和 componentWillUnmount 的行为

所以你真的不需要组件中的生命周期方法。效果挂钩正在取代它们。 =)

阅读上面的链接,您将获得一些关于它们如何工作的示例。

【讨论】:

好的。但是如果我想在 hooks 中使用静态 getDerivedStateFromProps 和 shouldComponentUpdate 方法呢?【参考方案3】:

React 团队为此提供了一个 useEffect 钩子。让我们以您的示例中的组件为例,并为计数添加服务器上传,否则我们将放入例如componentDidUpdate:

 import  useState, useEffect  from 'react';

 function Example() 
   const [count, setCount] = useState(0);
   useEffect(() => 
     fetch(
       'server/url',
       
         headers: 
           'Accept': 'application/json',
           'Content-Type': 'application/json'
         ,
         body: JSON.stringify(count),
       
     ); 
   );

   return (
     <div>
       <p>You clicked count times</p>
       <button onClick=() => setCount(count + 1)>
         Click me
       </button>
     </div>
   );
 

在这个例子中,这似乎不是一个巨大的胜利,因为它不是。但是生命周期方法的问题在于,您只能在组件中获得其中的每一个。如果你想上传到服务器,触发一个事件,把一条消息放到队列中,而这些都没有关系呢?太糟糕了,他们都挤在componentDidUpdate 中。或者你有 n 包裹的 HOC 层用于你想要做的 n 事情。但是使用钩子,您可以将所有这些拆分为对 useEffect 的解耦调用,而无需不必要的 HOC 层。

【讨论】:

可以多次使用useEffect吗?还有钩子中的静态 getDerivedStateFromProps 和 shouldComponentUpdate 怎么样? @Think-Twice 是的,您可以根据需要在组件定义中使用尽可能多的对useEffect 的调用。对于shouldComponentUpdate,该函数有一个可选的第二个参数,它是要观察的值的列表,例如useEffect(uploadToTheServer, [count])。我不知道 getDerivedStateFromProps 因为它运行 before 渲染。您可能仍然需要为该 IDK 使用一个类,这对我来说也是全新的。【参考方案4】:

功能组件是纯粹的无状态组件。但是在 React 16.8 中,他们添加了 Hooks。钩子可以用来代替状态和生命周期方法。

是的,你可以把 useEffectHook 想成 Like

componentDidMount

componentDidUpdate,

componentWillUnmount

shouldComponentUpdate 合并。

注意它是 componentDidMountcomponentDidUpdatecomponentWillUnmount & shouldComponentUpdate

的紧密替代品

【讨论】:

以上是关于如何在 React 中使用带有钩子的生命周期方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有 vuex 的 vue 生命周期钩子中使用 async/await?

Vue生命周期及钩子函数

React生命周期, setState、props改变触发的钩子函数

react生命周期钩子

Vue3 生命周期钩子函数

React Hooks 和组件生命周期等价物