How to use componentWillMount() in React Hooks?
在React的官方文档中,它提到-
If you’re familiar with React class lifecycle methods, you can think
of useEffect Hook as componentDidMount, componentDidUpdate, and
componentWillUnmount combined.
我的问题是-如何在钩子中使用
您不能在挂钩中使用任何现有的生命周期方法(
If you’re familiar with React class lifecycle methods, you can think of
useEffect Hook ascomponentDidMount ,componentDidUpdate , andcomponentWillUnmount combined.
建议是,您可以从功能组件中的类组件模仿这些生命周期方法。
装入组件后,
1 2 3 | useEffect(() => { // Your code here }, []); |
注意这里的第二个参数(空数组)。这将只运行一次。
如果没有第二个参数,
1 2 3 | useEffect(() => { // Your code here }); |
1 2 3 4 5 6 7 | componentDidMount() { window.addEventListener('mousemove', () => {}) } componentWillUnmount() { window.removeEventListener('mousemove', () => {}) } |
相当于上述代码的钩子如下
1 2 3 4 5 6 7 8 | useEffect(() => { window.addEventListener('mousemove', () => {}); // returned function will be called on component unmount return () => { window.removeEventListener('mousemove', () => {}) } }, []) |
useComponentDidMount挂钩
在大多数情况下,
1 | const useComponentDidMount = func => useEffect(func, []); |
useComponentWillMount
重要的是要注意,在类中,组件
1 2 3 4 5 6 7 8 9 | const useComponentWillMount = func => { const willMount = useRef(true); if (willMount.current) { func(); } willMount.current = false; }; |
但是,有一个陷阱。不要使用它来异步设置状态(例如,在服务器请求后进行。您可能希望它会影响初始渲染,而不会影响初始渲染)。此类情况应使用
演示版
1 2 3 4 5 6 7 8 9 | const Component = (props) => { useComponentWillMount(() => console.log("Runs only once before component mounts")); useComponentDidMount(() => console.log("Runs only once after component mounts")); ... return ( {...} ); } |
完整演示
根据reactjs.org所述,将来将不再支持componentWillMount。
https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
无需使用componentWillMount。
如果要在组件安装之前做一些事情,只需在构造函数()中进行即可。
如果要发出网络请求,请不要在componentWillMount中进行。这是因为这样做会导致意外的错误。
网络请求可以在componentDidMount中完成。
希望能帮助到你。
于08/03/2019更新
您要求componentWillMount的原因可能是因为您想要在渲染之前初始化状态。
只需在useState中执行即可。
1 2 3 4 5 6 7 | const helloWorld=()=>{ const [value,setValue]=useState(0) //initialize your state here return <p> {value} </p> } export default helloWorld; |
或者,例如,如果原始代码如下所示,则可能要在componentWillMount中运行一个函数:
1 2 3 | componentWillMount(){ console.log('componentWillMount') } |
使用钩子,您需要做的就是删除生命周期方法:
1 2 3 4 5 6 | const hookComponent=()=>{ console.log('componentWillMount') return <p> you have transfered componeWillMount from class component into hook </p> } |
我只想在有关useEffect的第一个答案中添加一些内容。
1 | useEffect(()=>{}) |
useEffect在每个渲染器上运行,它是componentDidUpdate,componentDidMount和ComponentWillUnmount的组合。
1 | useEffect(()=>{},[]) |
如果我们在useEffect中添加一个空数组,则它仅在安装组件时运行。这是因为useEffect将比较传递给它的数组。
因此它不必是一个空数组,它可以是不变的数组。例如,它可以是[1,2,3]或['1,2']。 useEffect仍然仅在安装组件时运行。
是否要运行一次还是在每次渲染后运行取决于您。只要您知道自己在做什么,就忘记添加数组,这并不危险。
我创建了一个挂钩示例。请检查一下。
https://codesandbox.io/s/kw6xj153wr
于21/08/2019更新
自从我写下以上答案以来,这一直是白人。我认为您需要注意一些事项。
使用时
1 | useEffect(()=>{},[]) |
当react比较您传递给数组[]的值时,它将使用Object.is()进行比较。
如果将对象传递给它,例如
1 | useEffect(()=>{},[{name:'Tom'}]) |
这与以下内容完全相同:
1 | useEffect(()=>{}) |
每次都会重新渲染,因为当Object.is()比较对象时,它将比较其引用而不是值本身。这与为什么{} === {}返回false的原因相同,因为它们的引用不同。
如果仍要比较对象本身而不是引用,则可以执行以下操作:
1 | useEffect(()=>{},[JSON.stringify({name:'Tom'})]) |
如果功能实际上与
例如:
1 2 3 4 5 6 7 | function MyComponent({ ...andItsProps }) { useLayoutEffect(()=> { console.log('I am about to render!'); },[]); return (some content); } |
使用初始化程序/设置程序或
我个人认为,
这是使用
1 2 3 4 5 6 7 8 9 | function Component(props) { const willMount = useRef(true); if (willMount.current) { console.log('This runs only once before rendering the component.'); willMount.current = false; } return (Meow world!); } |
这是生命周期示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 ( <> {count} <RenderLog>{count}</RenderLog> </> ); } |
1 2 3 4 5 6 7 8 | [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 |
当然,类组件没有
我编写了一个自定义钩子,它将在第一次渲染之前运行一次函数。
useBeforeFirstRender.js
1 2 3 4 5 6 7 8 9 10 11 | import { useState, useEffect } from 'react' export default (fun) => { const [hasRendered, setHasRendered] = useState(false) useEffect(() => setHasRendered(true), [hasRendered]) if (!hasRendered) { fun() } } |
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import React, { useEffect } from 'react' import useBeforeFirstRender from '../hooks/useBeforeFirstRender' export default () => { useBeforeFirstRender(() => { console.log('Do stuff here') }) return ( My component ) } |
有一个很好的解决方法,可以用
根据文档,
因此,如果我们使用完全没有依赖项的
1 2 3 4 5 6 7 | useEffect(() => { console.log('componentDidMount'); return () => { console.log('componentWillUnmount'); }; }, []); |
仅在卸载组件时才调用清除返回函数调用。
希望这可以帮助。
您可以修改useMemo挂钩以模仿componentWillMount生命周期事件。
做就是了:
1 2 3 4 5 6 7 8 9 10 11 | const Component = () => { useMemo(() => { // componentWillMount events },[]); useEffect(() => { // componentWillMount events return () => { // componentWillUnmount events } }, []); }; |
您需要先保留useMemo钩子,然后再与状态进行交互。 这不是它的预期目的,但是它对所有componentWillMount问题都有效。
之所以有效,是因为useMemo不需要实际返回值,您也不必实际将其用作任何值,但是由于它根据一个依赖项来存储一个值,该依赖项仅运行一次(" []"),并且其在我们组件的顶部运行 当组件安装在其他组件之前时,它将运行一次。
有一个简单的技巧通过使用
1 2 3 4 5 6 7 | useEffect(() => { console.log("componentDidMount"); return () => { console.log("componentWillUnmount"); }; }, []); |
只需在useEffect中添加一个空的Dependency数组,它将作为
1 2 3 4 | useEffect(() => { // Your code here console.log("componentDidMount") }, []); |
您最初的问题的简短答案,
已弃用
Generally, we recommend using the constructor() instead for initializing state.
现在,您可以在Hook FAQ中找到功能组件的类构造函数的等效项:
constructor: Function components don’t need a constructor. You can initialize the state in the useState call. If computing the initial state is expensive, you can pass a function to useState.
因此
1 2 3 4 5 6 7 8 | const MyComp = () => { const [state, setState] = useState(42) // set initial value directly in useState const [state2, setState2] = useState(createInitVal) // call complex computation return {state},{state2} }; const createInitVal = () => { /* ... complex computation or other logic */ return 42; }; |
本·卡尔普(Ben Carp)的答案对我来说似乎只有一个。
但是,由于我们使用的是函数式方法,因此只有另一种方法可以从闭包和HoC中受益:
1 2 3 4 5 6 7 8 9 10 | const InjectWillmount = function(Node, willMountCallback) { let isCalled = true; return function() { if (isCalled) { willMountCallback(); isCalled = false; } return Node; }; }; |
然后使用它:
1 2 3 | const YourNewComponent = InjectWillmount(<YourComponent />, () => { console.log("your pre-mount logic here"); }); |
https://reactjs.org/docs/hooks-reference.html#usememo
Remember that the function passed to useMemo runs during rendering.
Don’t do anything there that you wouldn’t normally do while rendering.
For example, side effects belong in useEffect, not useMemo.