在React的官方文档中提到-
如果您熟悉React类的生命周期方法,可以考虑 useEffect Hook作为componentDidMount,componentDidUpdate和 componentWillUnmount组合。
我的问题是-我们如何在钩子中使用componentWillMount()
lifecyle方法?
答案 0 :(得分:57)
您不能在钩子中使用任何现有的生命周期方法(componentDidMount,componentDidUpdate,componentWillUnmount等)。它们只能在类组件中使用。和挂钩只能在功能组件中使用。下面来自React文档
如果您熟悉React类的生命周期方法,则可以将useEffect Hook看作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', () => {})
}
}, [])
答案 1 :(得分:14)
这是我使用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仿真。
答案 2 :(得分:10)
您可以破解useMemo挂钩来模仿componentWillMount生命周期事件。 只要做:
const Component = () => {
useMemo(() => {
// componentWillMount events
},[]);
useEffect(() => {
// componentDidMount events
return () => {
// componentWillUnmount events
}
}, []);
};
在与状态交互的任何内容之前,您都需要保留useMemo挂钩。这不是它的预期目的,但是它对所有componentWillMount问题都有效。
之所以可行,是因为useMemo不需要实际返回值,并且您不必实际将其用作任何值,但是由于它基于仅运行一次(“ []”)的依赖关系来存储一个值,因此它会位于我们的组件在其他组件挂载之前会运行一次。
答案 3 :(得分:9)
为了简单的视觉参考,请跟随这张图片
如上图所示,对于ComponentWillUnmount,你必须这样做
useEffect(() => {
return () => {
console.log('componentWillUnmount');
};
}, []);
答案 4 :(得分:8)
根据reactjs.org,将来将不再支持componentWillMount。 https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
无需使用componentWillMount。
如果要在组件安装之前做一些事情,只需在Constructor()中做。
如果要发出网络请求,请不要在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>
}
我只想在有关useState的第一个答案中添加一些内容
useEffect(()=>{})
useEffect在每个渲染器上运行,它是componentDidUpdate,componentDidMount和ComponentWillUnmount的组合。
useEffect(()=>{},[])
如果我们在useEffect中添加一个空数组,它将在组件安装时运行。这是因为useEffect将比较传递给它的数组。 因此它不必是一个空数组,它可以是不变的数组。例如,它可以是[1,2,3]或['1,2']。 useEffect仍然仅在安装组件时运行。
这取决于您是希望它只运行一次还是在每次渲染后运行。只要您知道自己在做什么,就忘记添加数组就没有危险。
我创建了一个钩子样本。请检查一下。
答案 5 :(得分:3)
useLayoutEffect
的功能实际上与[]
类似,则可以使用一组空的观察者(componentWillMount
)来完成此操作-该功能将在第一个内容到达DOM之前运行- -尽管实际上有两个更新,但是在绘制到屏幕之前它们是同步的。
例如:
function MyComponent({ ...andItsProps }) {
useLayoutEffect(()=> {
console.log('I am about to render!');
},[]);
return (<div>some content</div>);
}
与初始化器/设置器或useState
相比,useEffect
的好处是尽管它可以计算渲染过程,但用户不会注意到对DOM的实际重新渲染,这是在第一个引人注意的渲染器之前运行 before ,useEffect
则不是这样。缺点当然是您的第一次渲染会稍有延迟,因为在绘画到屏幕之前必须进行检查/更新。不过,这确实取决于您的用例。
我个人认为,useMemo
在某些利基情况下需要做一些繁重的工作-只要记住,这是例外而不是常规。
答案 6 :(得分:1)
使用componentDidMount
实现componentWillUnmount
和useEffect
有很好的解决方法。
根据文档,useEffect
可以返回“清理”功能。此函数不会在第一个useEffect
调用中被调用,仅在后续调用中被调用。
因此,如果我们使用完全没有依赖项的useEffect
钩子,则仅在安装组件时才调用该钩子,而在卸载该组件时才调用“ cleanup”函数。
useEffect(() => {
console.log('componentDidMount');
return () => {
console.log('componentWillUnmount');
};
}, []);
仅当卸载组件时才调用清除返回函数调用。
希望这会有所帮助。
答案 7 :(得分:1)
componentWillMount
已弃用(1、2、3),并且建议的替换正在执行 constructor
在功能组件的主体中只运行一次函数。这可以通过 useState
、useMemo
或 useEffect
实现,具体取决于用例所需的时间。
由于代码需要在初始渲染提交到屏幕之前运行,因此 useEffect
不合格,因为“传递给 useEffect 的函数将在渲染提交到屏幕后运行。” 4。
因为我们想保证代码只运行一次,所以这不符合 useMemo
,因为“在未来,React 可能会选择‘忘记’一些之前记忆的值并在下一次渲染时重新计算它们”{{ 3}}。
useState
支持 5 计算,这些计算保证在初始渲染期间只运行一次,这似乎是这项工作的不错选择。
const runOnceBeforeRender = () => {};
const Component = () => {
useState(runOnceBeforeRender);
return (<></>);
}
const runOnceBeforeRender = () => {};
const useOnInitialRender = (fn) => {
useState(fn);
}
const Component = () => {
useOnInitialRender(fn);
return (<></>);
};
runOnceBeforeRender
函数可以选择返回一个状态,该状态将在函数第一次渲染时立即可用,不会触发重新渲染。
答案 8 :(得分:0)
https://reactjs.org/docs/hooks-reference.html#usememo
请记住,传递给useMemo的函数在渲染期间运行。 在该处不要执行渲染时通常不会执行的任何操作。 例如,副作用属于useEffect,而不是useMemo。
答案 9 :(得分:0)
在大多数情况下,useComponentDidMount
是要使用的工具。组件安装(初始渲染)后,它将仅运行一次。
const useComponentDidMount = func => useEffect(func, []);
重要的是要注意,在类组件componentWillMount
中被认为是遗留的。如果您需要代码在组件安装之前仅运行一次,则可以使用构造函数。 More about it here。
由于功能组件并不等同于构造函数,因此在某些情况下,使用钩子在组件挂载之前仅运行一次代码可能是有意义的。 useComponentWillMount
仅在第一次从组件中调用后才会立即运行。但是,不要传递包含副作用的回调(例如服务器调用),因为我们不想延迟组件的渲染。这些应该传递给useComponentDidMount
。
const useComponentWillMount = func => {
const [willMount, setWillMount] = useState(true);
useComponentDidMount(() => setWillMount(false));
if (willMount) {
func();
}
};
const Component = (props) => {
useComponentWillMount(() => console.log("Runs only once before component mounts"));
useComponentDidMount(() => console.log("Runs only once after component mounts"));
...
return (
<div>
{...}
</div>
);
}
答案 10 :(得分:0)
我写了一个自定义钩子,它将在第一次渲染之前运行一次函数。
useBeforeFirstRender.js
@types/node
用法:
import { useState, useEffect } from 'react'
export default (fun) => {
const [hasRendered, setHasRendered] = useState(false)
useEffect(() => setHasRendered(true), [hasRendered])
if (!hasRendered) {
fun()
}
}
答案 11 :(得分:0)
本·卡尔普(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");
});
答案 12 :(得分:0)
您的原始问题的简短答案,如何将componentWillMount
与React Hooks一起使用:
componentWillMount
是deprecated and considered legacy。反应recommendation:
通常,我们建议使用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; };
答案 13 :(得分:0)
正如react document中所述:
<块引用>您可能认为我们需要一个单独的效果来执行 清理。但是添加和删除订阅的代码是如此紧密 相关的是 useEffect 旨在将它保持在一起。如果你的效果 返回一个函数,React 会在需要清理时运行它:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
所以我们需要在钩子中使用 componentWillUnmount 的唯一事情就是在 useEffect 中返回一个函数,如上所述。
答案 14 :(得分:0)
所以对于 React 钩子,我认为在 return 语句可以工作之前声明你的逻辑。您应该有一个默认设置为 true 的状态。就我而言,我调用了状态 componentWillMount。然后有条件地在此状态为真时运行代码块(代码块包含您要在 componentWillMount 中执行的逻辑),此块中的最后一条语句应将 componentWillMountState 重置为 false(这一步很重要,因为如果它没有完成,会发生无限渲染) 示例
// do your imports here
const App = () => {
useEffect(() => {
console.log('component did mount')
}, [])
const [count, setCount] = useState(0);
const [componentWillMount, setComponentWillMount] = useState(true);
if (componentWillMount) {
console.log('component will mount')
// the logic you want in the componentWillMount lifecycle
setComponentWillMount(false)
}
return (
<div>
<div>
<button onClick={() => setCount(count + 1)}>
press me
</button>
<p>
{count}
</p>
</div>
</div>
)
}
答案 15 :(得分:0)
这可能不是 componentWillMount 方法的确切替代方法,但这里有一个方法可以用来实现相同的目标,但使用 useEffect :
首先将您检索数据的对象初始化为空值并定义 useEffect 方法:
const [details, setDetails] = useState("")
useEffect(() => {
retrieveData();
}, []);
const retrieveData = () => {
getData() // get data from the server
.then(response => {
console.log(response.data);
setDetails(response.data)
})
.catch(e => {
console.log(e);
})
}
现在在我们返回的 JSX 中添加一个三元运算符
*return(
<div>
{
details ? (
<div class="">
<p>add Your Jsx Here</p>
</div>
): (
<div>
<h4>Content is still Loading.....</h4>
</div>
)
}
</div>
)*
这将确保在对象 'details' 中有数据之前,三元运算符的第二部分被加载,这反过来会触发 useEffect 方法,该方法导致设置从服务器接收到的数据到 'details' 对象中,因此主 JSX 的渲染
答案 16 :(得分:-1)
对于大多数人来说可能很清楚,但请记住,在功能组件主体内部调用的功能充当 beforeRender 。这不能回答在ComponentWillMount上运行代码的问题(在第一个渲染之前),但是由于它是相关的,可能会帮助其他人,因此我将其留在这里。
const MyComponent = () => {
const [counter, setCounter] = useState(0)
useEffect(() => {
console.log('after render')
})
const iterate = () => {
setCounter(prevCounter => prevCounter+1)
}
const beforeRender = () => {
console.log('before render')
}
beforeRender()
return (
<div>
<div>{counter}</div>
<button onClick={iterate}>Re-render</button>
</div>
)
}
export default MyComponent
答案 17 :(得分:-3)
只需在useEffect中添加一个空的dependency数组,它将像componentDidMount
一样工作。
useEffect(() => {
// Your code here
console.log("componentDidMount")
}, []);