如何在React Hooks中使用componentWillMount()?

Abr*_*rar 69 javascript jsx reactjs react-hooks

在React的官方文档中提到 -

如果您熟悉React类生命周期方法,则可以将useEffect Hook视为componentDidMount,componentDidUpdate和componentWillUnmount的组合.

我的问题是 - 我们如何componentWillMount()在钩子中使用lifecyle方法?

Bha*_*han 146

您不能在钩子中使用任何现有的生命周期方法(componentDidMount,componentDidUpdate,componentWillUnmount等).它们只能在类组件中使用.而Hooks只能用于功能组件.React doc下面的内容是什么

如果您熟悉React类生命周期方法,则可以将useEffect Hook视为componentDidMount,componentDidUpdate和componentWillUnmount的组合.

建议是,您可以在功能组件中从类组件模仿这些生命周期方法.

componentDidMount中的代码在安装组件时运行一次.useEffect挂钩等效于此行为

useEffect(() => {
  // Your code here
}, []);
Run Code Online (Sandbox Code Playgroud)

注意这里的第二个参数(空数组).这只会运行一次.

如果没有第二个参数,将在组件的每个渲染上调用useEffect钩子,这可能是危险的.

useEffect(() => {
  // Your code here
});
Run Code Online (Sandbox Code Playgroud)

componentWillUnmount用于清理(如删除事件列表器,取消计时器等).假设您要在componentDidMount中添加事件列表器,并在componentWillUnmount中将其重新转移,如下所示.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}
Run Code Online (Sandbox Code Playgroud)

Hook相当于上面的代码

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
Run Code Online (Sandbox Code Playgroud)

  • 为什么这是一个被接受的答案?您没有提到“componentWillMount”的等效钩子 (158认同)
  • 对于其他Lifecycle事件的很好的解释,但这不是专门针对componentWillMount()的替代方案的问题。 (62认同)
  • @techexpert 该问题要求等效于“componentWillMount”,而不是“componentWillUnmount”。这个答案确实根本没有回答这个问题,只是重申了OP已经暗示要知道的内容。 (30认同)
  • 我现在真正理解了 `useEffect()` 函数,谢谢。 (4认同)
  • 在 PanResponder 示例中,我看到 componentWillMount 似乎是必需的,否则您会得到未定义的 panHandlers。 (3认同)
  • @techexpert实际上,函数组件中 return 语句之前的任何代码实际上都会在每次渲染组件时执行,而不仅仅是第一次使用构造函数(或之前的“componentWillMount”)。@Ben Carp 的答案是实际回答问题的得票最高的答案。 (3认同)
  • 那些说他没有提到“componentWillUnmount”等价物的人没有注意,特别是 useEffect 挂钩的“return”语句,您可以在其中执行所有“卸载”相关任务 (2认同)
  • 对于寻找所提出问题的实际答案的人,请参阅 /sf/ask/3742521681/#67315083 或博客文章 https: //dev.to/video/the-equivalent-of-componentwillmount-using-react-hooks-11em,因为大量使用 useEffect 并不等同于 componentWillMount。 (2认同)

MIN*_* WU 31

根据reactjs.org所述,将来将不再支持componentWillMount。 https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

无需使用componentWillMount。

如果要在组件安装之前做一些事情,只需在构造函数()中进行即可。

如果要发出网络请求,请不要在componentWillMount中进行。这是因为这样做会导致意外的错误。

网络请求可以在componentDidMount中完成。

希望能帮助到你。


于08/03/2019更新

您要求componentWillMount的原因可能是因为您想在渲染之前初始化状态。

只需在useState中执行即可。

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;
Run Code Online (Sandbox Code Playgroud)

或者,例如,如果原始代码如下所示,则可能要在componentWillMount中运行一个函数:

componentWillMount(){
  console.log('componentWillMount')
}
Run Code Online (Sandbox Code Playgroud)

使用钩子,您需要做的就是删除生命周期方法:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}
Run Code Online (Sandbox Code Playgroud)

我只想在有关useEffect的第一个答案中添加一些内容。

useEffect(()=>{})
Run Code Online (Sandbox Code Playgroud)

useEffect在每个渲染器上运行,它是componentDidUpdate,componentDidMount和ComponentWillUnmount的组合。

 useEffect(()=>{},[])
Run Code Online (Sandbox Code Playgroud)

如果我们在useEffect中添加一个空数组,则它仅在安装组件时运行。这是因为useEffect将比较传递给它的数组。因此它不必是一个空数组,它可以是不变的数组。例如,它可以是[1,2,3]或['1,2']。useEffect仍然仅在安装组件时运行。

是否要运行一次还是在每次渲染后运行取决于您。只要您知道自己在做什么,就忘记添加数组,这并不危险。

我创建了一个挂钩示例。请检查一下。

https://codesandbox.io/s/kw6xj153wr


于21/08/2019更新

自从我写下以上答案以来,这一直是白人。我认为您需要注意一些事项。使用时

useEffect(()=>{},[])
Run Code Online (Sandbox Code Playgroud)

当react比较您传递给数组[]的值时,它使用Object.is()进行比较。如果将对象传递给它,例如

useEffect(()=>{},[{name:'Tom'}])
Run Code Online (Sandbox Code Playgroud)

这与以下内容完全相同:

useEffect(()=>{})
Run Code Online (Sandbox Code Playgroud)

每次都会重新渲染,因为当Object.is()比较对象时,它将比较其引用而不是值本身。这与{} === {}返回false的原因相同,因为它们的引用不同。如果仍然要比较对象本身而不是引用,则可以执行以下操作:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])
Run Code Online (Sandbox Code Playgroud)

  • 问题是如何用钩子实现它 (8认同)
  • 这和 componentWillMount 怎么是一样的?如果您将代码放入功能组件中,那么它将运行每个渲染,而不仅仅是在组件即将安装时运行。 (5认同)
  • 当然,这应该是可以接受的答案-它说明了在钩子范例中ComponentWillMount不可用。功能组件中的初始化得到简化-只需将其作为功能的一部分 (4认同)
  • 但是您不需要使用钩子来实现它,因为它不受支持。无需学习如何使用钩子进行操作。 (3认同)

jpm*_*rks 18

您可以破解 useMemo 钩子来模拟 componentWillMount 生命周期事件。做就是了:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentDidMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};
Run Code Online (Sandbox Code Playgroud)

您需要在与您的状态交互的任何内容之前保留 useMemo 钩子。这不是它的意图,但它对我所有 componentWillMount 问题都有效。

这是有效的,因为 useMemo 不需要实际返回一个值,您实际上不必将它用作任何东西,但是因为它根据依赖项记住一个值,该值只会运行一次(“[]”)并且它位于我们的组件之上当组件在其他任何事情之前安装时运行一次。

  • 这可能适用于当前的实现,但 React 文档明确指出不要这样做。**您可以依赖 useMemo 作为性能优化,而不是语义保证。** https://reactjs.org/docs/hooks-reference.html#usememo (3认同)

Jir*_*hal 17

这是我如何使用useRef钩子在功能组件中模拟构造函数的方式:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}
Run Code Online (Sandbox Code Playgroud)

这是生命周期示例:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
Run Code Online (Sandbox Code Playgroud)
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props
Run Code Online (Sandbox Code Playgroud)

当然Class组件没有Body步骤,由于函数和类的概念不同,无法进行1:1的模拟。


lok*_*ngh 13

钩子中的 React Lifecycle 方法

为了简单的视觉参考,请遵循此图像

在此处输入图片说明

正如您在上图中可以简单地看到,对于 ComponentWillUnmount,您必须这样做

 useEffect(() => {
    return () => {
        console.log('componentWillUnmount');
    };
   }, []);
Run Code Online (Sandbox Code Playgroud)

  • 问题是针对“componentWillMount”,而不是“componentWillUnmount” (6认同)

Ben*_*arp 11

useComponentDidMount挂钩

在大多数情况下useComponentDidMount是要使用的工具。组件安装(初始渲染)后,它将仅运行一次。

 const useComponentDidMount = func => useEffect(func, []);
Run Code Online (Sandbox Code Playgroud)

useComponentWillMount

重要的是要注意,在类中组件componentWillMount被认为是遗留的。如果您需要代码在组件安装之前仅运行一次,则可以使用构造函数。在这里了解更多。由于功能组件没有构造函数的等效性,因此在某些情况下使用钩子在组件挂载前仅运行一次代码可能是有意义的。您可以使用自定义钩子来实现。

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  useComponentDidMount(() => {
    willMount.current = false;
  });
};
Run Code Online (Sandbox Code Playgroud)

但是,有一个陷阱。不要使用它来异步设置状态(例如在服务器请求之后。如您所料,它会影响初始渲染,而不会影响初始渲染)。此类情况应以处理useComponentDidMount

演示版

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}
Run Code Online (Sandbox Code Playgroud)

完整演示

  • 这是回答问题并且有意义的唯一答案。谢谢你! (44认同)
  • 这种基于 useEffect 的 componentWillMount 功能实现有两个问题。第一个是功能组件中没有安装生命周期,两个钩子都会在组件渲染后运行,因此“在组件安装之前仅运行一次”具有误导性。第二个是在服务器渲染时调用了“componentWillMount”,而没有调用“useEffect”。许多库仍然依赖 [`UNSAFE_componentWillMount`](https://reactjs.org/docs/react-component.html#unsafe_componentwillmount),因为目前它是触发服务器端副作用的唯一方法。 (3认同)
  • @PaoloMoretti,谢谢。这个 componentWillMount 钩子并不完全等同于类组件中的 componentWillMount 生命周期。但是,传递给它的函数只会在第一次调用时立即运行。这实际上意味着它将在渲染之前运行,甚至在第一次返回值之前运行。我们可以同意吗?我同意使用名称 componentWillMount 并不理想,因为该名称具有类生命周期版本中的某些含义。也许我更好地称之为“useRunPreMount”。 (3认同)
  • 唯一的问题是,由于涉及状态更新,您将获得额外的渲染。通过使用ref,您可以得到所需的行为而无需额外的渲染:`const useComponentWillMount = func =&gt; {const willMount = useRef(true); useEffect(()=&gt; {willMount.current = false;},[]); 如果(willMount.current){func(); }; ` (2认同)

rob*_*b2d 9

useLayoutEffect[]如果功能实际上类似于componentWillMount-它可以在第一个内容到达DOM之前运行,则可以使用一组空的观察者()完成此操作–尽管实际上有两个更新,但是它们在绘制到屏幕之前是同步的。

例如:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

Run Code Online (Sandbox Code Playgroud)

useState对于使用初始化程序/设置程序的好处,或者useEffect尽管它可以计算渲染过程,但用户不会注意到对DOM的实际重新渲染,而是第一个值得注意的渲染之前运行,而事实并非如此。useEffect。缺点当然是您的第一次渲染会稍有延迟,因为必须先进行检查/更新,然后才能绘画到屏幕上。不过,这确实取决于您的用例。

我个人认为,useMemo在某些小众情况下,您需要做一些繁重的事情是可以的-只要记住,这是例外而不是常规。

  • useLayoutEffect是必经之路!!!!该问题回答了我关于检查用户是否已登录的问题。(问题是,将加载组件,然后检查用户是否已登录。)我的问题是,这是标准做法吗?我没看到太多地方 (2认同)

Lit*_*ime 6

我写了一个自定义钩子,它将在第一次渲染之前运行一次函数。

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => { 
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)


mar*_*ark 5

鉴于

\n
    \n
  • componentWillMount已弃用(1 , 2 , 3),并且建议的替换是在 constructor
  • \n
  • 在函数组件中,在 return 语句之前执行的代码会在渲染之前隐式运行
  • \n
  • 大致相当于安装类组件是功能组件的初始调用
  • \n
  • 目标是在更新 DOM ui 之前执行一段代码,并且只要组件保持“已安装”状态,就不再运行该代码
  • \n
\n

解决方案是

\n

仅在功能组件主体中运行一次函数。由于功能组件主体中的代码通常在每次重新渲染时运行,因此我们需要转向钩子。选项为useStateuseMemouseEffect,具体取决于用例所需的时间。

\n

由于代码需要在初始渲染提交到屏幕之前useEffect运行,因此这不合格: \xe2\x80\x9c 传递给 useEffect 的函数将在渲染提交到屏幕后运行。\xe2\x80\x9d 4

\n

由于我们要保证代码只会运行一次并且不会被重新计算,因此这不符合条件useMemo:“将来,React 可能会选择 \xe2\x80\x9c 忘记\xe2\x80\x9d 一些以前记忆的值,并在下一次重新计算它们渲染” 5 .

\n

useState支持惰性初始状态计算,保证在首次渲染功能组件时仅运行一次,这正是我们正在寻找的。

\n

useState 的示例

\n
const runOnceBeforeRender = () => {};\n\nconst Component = () => {\n  useState(runOnceBeforeRender);\n\n  return (<></>);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

作为自定义钩子

\n
const runOnceBeforeRender = () => {};\n\nconst useOnInitialRender = (fn) => {\n  useState(fn);\n}\n\nconst Component = () => {\n  useOnInitialRender(runOnceBeforeRender);\n\n  return (<></>);\n};\n
Run Code Online (Sandbox Code Playgroud)\n

runOnceBeforeRender函数可以选择返回一个在函数第一次渲染时立即可用的值,而不会触发重新渲染。该值不会在后续渲染中重新计算。

\n
const runOnceBeforeRender = () => 1 + 1;\n\nconst useOnInitialRender = (fn) => {\n  const [value] = useState(fn);\n\n  return value;\n}\n\nconst Component = () => {\n  const value = useOnInitialRender(runOnceBeforeRender);\n\n  return (<>{value}</>); // 2\n};\n\n
Run Code Online (Sandbox Code Playgroud)\n

一个(可能不必要的)NPM 包:useOnce 钩子

\n