Answers:
你不能使用任何现有的生命周期方法(componentDidMount
,componentDidUpdate
,componentWillUnmount
在钩等)。它们只能在类组件中使用。而且,使用Hooks只能在功能组件中使用。下面的行来自React文档:
如果你熟悉阵营类生命周期方法,你能想到的
useEffect
钩。因为componentDidMount
,componentDidUpdate
和componentWillUnmount
结合。
建议是,您可以从功能组件中的类组件模仿这些生命周期方法。
componentDidMount
安装组件时,内部代码运行一次。useEffect
钩子等效于此行为是
useEffect(() => {
// Your code here
}, []);
注意此处的第二个参数(空数组)。这将只运行一次。
如果没有第二个参数,useEffect
则会在每次渲染组件时调用该钩子,这很危险。
useEffect(() => {
// Your code here
});
componentWillUnmount
用于清理(例如删除事件侦听器,取消计时器等)。假设您要在其中添加事件监听器,componentDidMount
并按componentWillUnmount
如下所示将其删除。
componentDidMount() {
window.addEventListener('mousemove', () => {})
}
componentWillUnmount() {
window.removeEventListener('mousemove', () => {})
}
相当于上述代码的钩子如下
useEffect(() => {
window.addEventListener('mousemove', () => {});
// returned function will be called on component unmount
return () => {
window.removeEventListener('mousemove', () => {})
}
}, [])
useEffect()
功能,谢谢。
componentWillMount
componentWillMount
,而不是componentWillUnmount
。确实,这个答案根本不能回答问题,只是重申了OP已经暗示要知道的内容。
在大多数情况下useComponentDidMount
是要使用的工具。组件已安装(初始渲染)后,它将仅运行一次。
const useComponentDidMount = func => useEffect(func, []);
重要的是要注意,在类中组件componentWillMount
被认为是遗留的。如果您需要代码在组件安装之前仅运行一次,则可以使用构造函数。在这里了解更多。由于功能组件没有构造函数的等效性,因此在某些情况下使用钩子在组件挂载前仅运行一次代码可能是有意义的。您可以使用自定义钩子来实现。
const useComponentWillMount = func => {
const willMount = useRef(true);
if (willMount.current) {
func();
}
willMount.current = false;
};
但是,有一个陷阱。不要使用它来异步设置状态(例如在服务器请求之后。如您所料,它会影响初始渲染,而不会影响初始渲染)。此类情况应以处理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>
);
}
componentWillMount
基于的此功能实现 useEffect
有两个问题。第一个是功能组件中没有安装生命周期,两个挂钩都将在组件渲染后运行,因此Runs only once before component mounts
具有误导性。第二个是componentWillMount
在服务器渲染上调用的,useEffect
不是。许多库仍然依赖于此,UNSAFE_componentWillMount
因为当前这是触发服务器端副作用的唯一方法。
根据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;
或者,例如,如果原始代码如下所示,则可能要在componentWillMount中运行一个函数:
componentWillMount(){
console.log('componentWillMount')
}
使用钩子,您需要做的就是删除生命周期方法:
const hookComponent=()=>{
console.log('componentWillMount')
return <p>you have transfered componeWillMount from class component into hook </p>
}
我只想在有关useEffect的第一个答案中添加一些内容。
useEffect(()=>{})
useEffect在每个渲染器上运行,它是componentDidUpdate,componentDidMount和ComponentWillUnmount的组合。
useEffect(()=>{},[])
如果我们在useEffect中添加一个空数组,则它仅在安装组件时运行。这是因为useEffect将比较传递给它的数组。因此它不必是一个空数组,它可以是不变的数组。例如,它可以是[1,2,3]或['1,2']。useEffect仍然仅在安装组件时运行。
是否要运行一次还是在每次渲染后运行取决于您。只要您知道自己在做什么,就忘记添加数组,这并不危险。
我创建了一个挂钩示例。请检查一下。
https://codesandbox.io/s/kw6xj153wr
于21/08/2019更新
自从我写下以上答案以来,这一直是白人。我认为您需要注意一些事项。使用时
useEffect(()=>{},[])
当react比较您传递给数组[]的值时,它将使用Object.is()进行比较。如果将对象传递给它,例如
useEffect(()=>{},[{name:'Tom'}])
这与以下内容完全相同:
useEffect(()=>{})
每次都会重新渲染,因为当Object.is()比较对象时,它将比较其引用而不是值本身。这与为什么{} === {}返回false的原因相同,因为它们的引用不同。如果仍要比较对象本身而不是引用,则可以执行以下操作:
useEffect(()=>{},[JSON.stringify({name:'Tom'})])
useLayoutEffect
[]
如果功能实际上类似于componentWillMount
-它可以在第一个内容到达DOM之前运行,则可以使用一组空的观察者()完成此操作–尽管实际上有两个更新,但是它们在绘制到屏幕之前是同步的。
例如:
function MyComponent({ ...andItsProps }) {
useLayoutEffect(()=> {
console.log('I am about to render!');
},[]);
return (<div>some content</div>);
}
相useState
对于使用初始化程序/设置程序的好处,或者useEffect
尽管它可以计算渲染过程,但用户不会注意到对DOM的实际重新渲染,而是在第一个值得注意的渲染之前运行,而不是这样。useEffect
。缺点当然是您的第一次渲染会稍有延迟,因为在绘画到屏幕之前必须进行检查/更新。不过,这确实取决于您的用例。
我个人认为,useMemo
在某些需要进行繁重工作的特殊情况下,这很好-只要记住,这是例外而不是常规。
我编写了一个自定义钩子,它将在第一次渲染之前运行一次函数。
useBeforeFirstRender.js
import { useState, useEffect } from 'react'
export default (fun) => {
const [hasRendered, setHasRendered] = useState(false)
useEffect(() => setHasRendered(true), [hasRendered])
if (!hasRendered) {
fun()
}
}
用法:
import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'
export default () => {
useBeforeFirstRender(() => {
console.log('Do stuff here')
})
return (
<div>
My component
</div>
)
}
这是我使用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>);
}
这是生命周期示例:
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>
</>
);
}
[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仿真。
有一个很好的解决方法来实现componentDidMount
和componentWillUnmount
使用useEffect
。
根据文档,useEffect
可以返回“清理”功能。此函数将不会在第一个useEffect
调用中被调用,仅在后续调用中被调用。
因此,如果我们使用useEffect
完全不依赖的钩子,则仅在安装组件时才调用该钩子,而在卸载该组件时才调用“ cleanup”函数。
useEffect(() => {
console.log('componentDidMount');
return () => {
console.log('componentWillUnmount');
};
}, []);
仅在卸载组件时才调用清除返回函数调用。
希望这可以帮助。
useEffect
叫你得到相同的功能componentWillMount
,并componentWillUnmount
在一个非常干净的方式
useEffect
仅在渲染之后运行,而componentWillMount
在组件渲染之前运行。
componentDidMount
不是componentWillMount
。我错过了这个问题,我很糟糕。
您可以修改useMemo挂钩以模仿componentWillMount生命周期事件。做就是了:
const Component = () => {
useMemo(() => {
// componentWillMount events
},[]);
useEffect(() => {
// componentWillMount events
return () => {
// componentWillUnmount events
}
}, []);
};
您需要先保留useMemo钩子,然后再与状态进行交互。这不是它的预期目的,但是它对所有componentWillMount问题都有效。
之所以有效,是因为useMemo不需要实际返回值,并且您不必实际将其用作任何值,但是由于它根据一个依赖项存储一个值,该依赖项仅运行一次(“ []”),并且其在我们组件的顶部运行当组件安装在其他组件之前时,它将运行一次。
https://reactjs.org/docs/hooks-reference.html#usememo
请记住,传递给useMemo的函数在渲染期间运行。不要在渲染时执行通常不会执行的任何操作。例如,副作用属于useEffect,而不是useMemo。
本·卡尔普(Ben Carp)的答案对我来说似乎只有一个。
但是,由于我们使用的是功能方式,因此可以从闭包和HoC中受益于另一种方法:
const InjectWillmount = function(Node, willMountCallback) {
let isCalled = true;
return function() {
if (isCalled) {
willMountCallback();
isCalled = false;
}
return Node;
};
};
然后使用它:
const YourNewComponent = InjectWillmount(<YourComponent />, () => {
console.log("your pre-mount logic here");
});
原始问题的简短答案,如何componentWillMount
与React Hooks一起使用:
componentWillMount
已弃用,并视为旧版。反应建议:
通常,我们建议使用constructor()代替初始化状态。
现在,您可以在Hook FAQ中找到功能组件的类构造函数的等效项:
构造函数:功能组件不需要构造函数。您可以在useState调用中初始化状态。如果计算初始状态的成本很高,则可以将一个函数传递给useState。
因此,一个使用示例componentWillMount
如下所示:
const MyComp = () => {
const [state, setState] = useState(42) // set initial value directly in useState
const [state2, setState2] = useState(createInitVal) // call complex computation
return <div>{state},{state2}</div>
};
const createInitVal = () => { /* ... complex computation or other logic */ return 42; };