关于javascript:如何在React Hooks中使用componentWillMount()?

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.

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


您不能在挂钩中使用任何现有的生命周期方法(componentDidMountcomponentDidUpdatecomponentWillUnmount等)。它们只能在类组件中使用。并且使用挂钩,您只能在功能组件中使用。下面的行来自React文档:

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

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

装入组件后,componentDidMount中的代码将运行一次。此行为的useEffect钩子等效项是

1
2
3
useEffect(() => {
  // Your code here
}, []);

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

如果没有第二个参数,useEffect挂钩将在每次渲染组件时被调用,这很危险。

1
2
3
useEffect(() => {
  // Your code here
});

componentWillUnmount用于清理(例如删除事件侦听器,取消计时器等)。假设您要在componentDidMount中添加事件侦听器,并在componentWillUnmount中将其删除,如下所示。

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挂钩

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

1
 const useComponentDidMount = func => useEffect(func, []);

useComponentWillMount

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

1
2
3
4
5
6
7
8
9
const useComponentWillMount = func => {
  const willMount = useRef(true);

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

  willMount.current = false;
};

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

演示版

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'})])


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

例如:

1
2
3
4
5
6
7
function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (some content);
}

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

我个人认为,useMemo在某些需要做一些繁重工作的特殊情况下很好-只要记住,这是例外而不是常规。


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

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

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


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

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
   
  )
}

有一个很好的解决方法,可以用useEffect实现componentDidMountcomponentWillUnmount

根据文档,useEffect可以返回"清理"功能。此函数不会在第一个useEffect调用上被调用,仅在后续调用上被调用。

因此,如果我们使用完全没有依赖项的useEffect挂钩,则仅在安装组件时才会调用该挂钩,而在卸载组件时将调用" cleanup"功能。

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不需要实际返回值,您也不必实际将其用作任何值,但是由于它根据一个依赖项来存储一个值,该依赖项仅运行一次(" []"),并且其在我们组件的顶部运行 当组件安装在其他组件之前时,它将运行一次。


有一个简单的技巧通过使用useEffect模拟componentDidMountcomponentWillUnmount

1
2
3
4
5
6
7
useEffect(() => {
  console.log("componentDidMount");

  return () => {
    console.log("componentWillUnmount");
  };
}, []);

只需在useEffect中添加一个空的Dependency数组,它将作为componentDidMount起作用。

1
2
3
4
useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);

您最初的问题的简短答案,componentWillMount如何与React Hooks一起使用:

已弃用componentWillMount,并认为它是旧版。反应建议:

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.

因此componentWillMount的用法示例如下所示:

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.