Yan*_*Tay 31 javascript reactjs react-hooks
根据文件:
componentDidUpdate()更新发生后立即调用.初始渲染不会调用此方法.
我们可以使用新的useEffect()钩子来模拟componentDidUpdate(),但似乎useEffect()是在每次渲染之后运行,甚至是第一次.如何让它不能在初始渲染上运行?
正如您在下面的示例中所看到的,componentDidUpdateFunction在初始渲染期间打印,但componentDidUpdateClass在初始渲染期间未打印.
function ComponentDidUpdateFunction() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log("componentDidUpdateFunction");
});
return (
<div>
<p>componentDidUpdateFunction: {count} times</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}
class ComponentDidUpdateClass extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
componentDidUpdate() {
console.log("componentDidUpdateClass");
}
render() {
return (
<div>
<p>componentDidUpdateClass: {this.state.count} times</p>
<button
onClick={() => {
this.setState({ count: this.state.count + 1 });
}}
>
Click Me
</button>
</div>
);
}
}
ReactDOM.render(
<div>
<ComponentDidUpdateFunction />
<ComponentDidUpdateClass />
</div>,
document.querySelector("#app")
);Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>Run Code Online (Sandbox Code Playgroud)
Tho*_*lle 47
我们可以使用useRef钩子来存储我们喜欢的任何可变值,因此我们可以使用它来跟踪它是否是第一次useEffect运行该函数.
如果我们希望效果在相同的阶段运行componentDidUpdate,我们可以useLayoutEffect改为使用.
例
const { useState, useRef, useLayoutEffect } = React;
function ComponentDidUpdateFunction() {
const [count, setCount] = useState(0);
const firstUpdate = useRef(true);
useLayoutEffect(() => {
if (firstUpdate.current) {
firstUpdate.current = false;
return;
}
console.log("componentDidUpdateFunction");
});
return (
<div>
<p>componentDidUpdateFunction: {count} times</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}
ReactDOM.render(
<ComponentDidUpdateFunction />,
document.getElementById("app")
);Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>Run Code Online (Sandbox Code Playgroud)
Mar*_*ais 33
我做了一个简单的useFirstRender钩子来处理像聚焦表单输入这样的情况:
import { useRef, useEffect } from 'react';
export function useFirstRender() {
const firstRender = useRef(true);
useEffect(() => {
firstRender.current = false;
}, []);
return firstRender.current;
}
Run Code Online (Sandbox Code Playgroud)
它以 开头true,然后切换到false中useEffect,它只运行一次,再也不会运行。
在您的组件中,使用它:
const firstRender = useFirstRender();
const phoneNumberRef = useRef(null);
useEffect(() => {
if (firstRender || errors.phoneNumber) {
phoneNumberRef.current.focus();
}
}, [firstRender, errors.phoneNumber]);
Run Code Online (Sandbox Code Playgroud)
对于您的情况,您只需使用if (!firstRender) { ....
Lui*_*igi 17
与Tholle 的答案相同的方法,但使用useState而不是useRef.
const [skipCount, setSkipCount] = useState(true);
...
useEffect(() => {
if (skipCount) setSkipCount(false);
if (!skipCount) runYourFunction();
}, [dependencies])
Run Code Online (Sandbox Code Playgroud)
编辑
虽然这也有效,但它涉及更新状态,这将导致您的组件重新渲染。如果您的所有组件的useEffect调用(以及它的所有子组件)都有一个依赖数组,那么这并不重要。但请记住,任何useEffect没有依赖项数组的 (useEffect(() => {...})都将再次运行。
使用和更新useRef不会导致任何重新渲染。
您可以将其变成自定义钩子,如下所示:
import React, { useEffect, useRef } from 'react';
const useDidMountEffect = (func, deps) => {
const didMount = useRef(false);
useEffect(() => {
if (didMount.current) func();
else didMount.current = true;
}, deps);
}
export default useDidMountEffect;
Run Code Online (Sandbox Code Playgroud)
用法示例:
import React, { useState, useEffect } from 'react';
import useDidMountEffect from '../path/to/useDidMountEffect';
const MyComponent = (props) => {
const [state, setState] = useState({
key: false
});
useEffect(() => {
// you know what is this, don't you?
}, []);
useDidMountEffect(() => {
// react please run me if 'key' changes, but not on initial render
}, [state.key]);
return (
<div>
...
</div>
);
}
// ...
Run Code Online (Sandbox Code Playgroud)
@ravi,你的没有调用传入的卸载函数。这是一个更完整的版本:
/**
* Identical to React.useEffect, except that it never runs on mount. This is
* the equivalent of the componentDidUpdate lifecycle function.
*
* @param {function:function} effect - A useEffect effect.
* @param {array} [dependencies] - useEffect dependency list.
*/
export const useEffectExceptOnMount = (effect, dependencies) => {
const mounted = React.useRef(false);
React.useEffect(() => {
if (mounted.current) {
const unmount = effect();
return () => unmount && unmount();
} else {
mounted.current = true;
}
}, dependencies);
// Reset on unmount for the next mount.
React.useEffect(() => {
return () => mounted.current = false;
}, []);
};Run Code Online (Sandbox Code Playgroud)
function useEffectAfterMount(effect, deps) {
const isMounted = useRef(false);
useEffect(() => {
if (isMounted.current) return effect();
else isMounted.current = true;
}, deps);
// reset on unmount; in React 18, components can mount again
useEffect(() => () => {
isMounted.current = false;
}, []);
}
Run Code Online (Sandbox Code Playgroud)
我们需要返回从effect(),因为它可能是一个清理函数。但我们不需要确定它是与否。把它传递出去,让我们useEffect弄清楚。
在这篇文章的早期版本中,我说过重置 ref ( isMounted.current = false) 是不必要的。但在 React 18 中确实如此,因为组件可以以其之前的状态重新挂载(感谢 @Whatabrain)。
| 归档时间: |
|
| 查看次数: |
12586 次 |
| 最近记录: |