componentDidMount等效React函数/Hooks组件?

有没有方法通过钩子来模拟React功能组件中的componentDidMount ?

156399 次浏览

你想要使用useEffect(),这取决于你如何使用函数,就像componentDidMount()一样。

如。你可以使用一个自定义的loaded状态属性,它最初被设置为false,并在呈现时将其切换为true,并且只在这个值改变时触发效果。

文档

函数组件上没有componentDidMount,但是React Hooks提供了一种方法,可以通过使用useEffect钩子来模拟行为。

将一个空数组作为第二个参数传递给useEffect(),以仅在挂载时运行回调。

请阅读useEffect的文档

function ComponentDidMount() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log('componentDidMount');
}, []);


return (
<div>
<p>componentDidMount: {count} times</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}


ReactDOM.render(
<div>
<ComponentDidMount />
</div>,
document.querySelector("#app")
);
<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>

对于钩子的稳定版本(React version 16.8.0+)

对于componentDidMount

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

对于componentDidUpdate

useEffect(() => {
// Your code here
}, [yourDependency]);

对于componentWillUnmount

useEffect(() => {
// componentWillUnmount
return () => {
// Your code here
}
}, [yourDependency]);

在这种情况下,你需要将你的依赖传递到这个数组中。假设有一个这样的状态

const [count, setCount] = useState(0);

当count增加时,你想重新渲染你的函数组件。然后你的useEffect应该看起来像这样

useEffect(() => {
// <div>{count}</div>
}, [count]);

这样,每当你的计数更新你的组件将重新呈现。希望这对你有所帮助。

虽然公认的答案是可行的,但不建议使用。当你有多个状态并且你和useEffect一起使用它时,它会警告你是否将它添加到依赖数组或者根本不使用它。

它有时会导致问题,可能会产生不可预测的输出。因此,我建议您花点精力将函数重写为类。有很少的变化,您可以将一些组件作为类,一些作为函数。您没有义务只使用一种约定。

以这个为例

function App() {
const [appointments, setAppointments] = useState([]);
const [aptId, setAptId] = useState(1);


useEffect(() => {
fetch('./data.json')
.then(response => response.json())
.then(result => {
const apts = result.map(item => {
item.aptId = aptId;
console.log(aptId);
setAptId(aptId + 1);
return item;
})
setAppointments(apts);
});
}, []);


return(...);
}

而且

class App extends Component {
constructor() {
super();
this.state = {
appointments: [],
aptId: 1,
}
}


componentDidMount() {
fetch('./data.json')
.then(response => response.json())
.then(result => {
const apts = result.map(item => {
item.aptId = this.state.aptId;
this.setState({aptId: this.state.aptId + 1});
console.log(this.state.aptId);
return item;
});
this.setState({appointments: apts});
});
}


render(...);
}

这只是举个例子。因此,让我们不要谈论代码的最佳实践或潜在问题。两者的逻辑相同,但后者只能按预期工作。此时,你可能会获得componentDidMount功能和useEffect,但随着应用程序的发展,你可能会面临一些问题。所以,与其在那个阶段重写,不如在早期就重写。

此外,面向对象并不是那么糟糕,如果面向过程编程就足够了,我们就永远不会有面向对象编程了。有时会很痛苦,但更好(技术上来说。暂且把个人问题放在一边)。

componentDidMount()的等效钩子是

useEffect(()=>{},[]);

希望这对你有帮助。

在react钩子中没有完全等价的componentDidMount


根据我的经验,react钩子在开发时需要不同的心态,一般来说,你不应该将它与componentDidMount这样的类方法进行比较。

也就是说,有几种方法可以使用钩子来产生类似的效果componentDidMount

解决方案1:

useEffect(() => {
console.log("I have been mounted")
}, [])

解决方案2:

const num = 5


useEffect(() => {
console.log("I will only run if my deps change: ", num)
}, [num])

方案三(带功能):

useEffect(() => {
const someFunc = () => {
console.log("Function being run after/on mount")
}
someFunc()
}, [])

解决方案4 (useCallback):

const msg = "some message"


const myFunc = useCallback(() => {
console.log(msg)
}, [msg])


useEffect(() => {
myFunc()
}, [myFunc])

解决方案5(变得有创意):

export default function useDidMountHook(callback) {
const didMount = useRef(null)


useEffect(() => {
if (callback && !didMount.current) {
didMount.current = true
callback()
}
})
}

值得注意的是只有当其他解决方案都不适合您的用例时,才应该真正使用解决方案5。如果你确实决定需要解决方案5,那么我建议使用这个预制钩子use-did-mount

来源(包含更多细节):在react钩子中使用componentDidMount

useEffect ()钩子允许我们实现componentDidMount功能,componentDidUpdate componentWillUnMount功能。

useEffect()的不同语法允许实现上述每一种方法。

我)componentDidMount

useEffect(() => {
//code here
}, []);

(二)componentDidUpdate

useEffect(() => {
//code here
}, [x,y,z]);


//where x,y,z are state variables on whose update, this method should get triggered

3) componentDidUnmount

useEffect(() => {
//code here
return function() {
//code to be run during unmount phase
}
}, []);

你可以查看官方react网站获取更多信息。hook的官方反应页面

关于钩子内异步函数的信息:

效果回调是同步的,以防止竞争条件。把async函数放在里面:

useEffect(() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
import React, { useState, useEffect } from 'react';


function Example() {
const [count, setCount] = useState(0);


// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});


return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

请访问这个官方文档。很容易理解最新的方法

https://reactjs.org/docs/hooks-effect.html

useLayoutEffect钩子是React钩子中ComponentDidMount的最佳替代 useLayoutEffect钩子在渲染UI之前执行,useEffect钩子在渲染UI之后执行。

示例代码:< / >强

import { useLayoutEffect, useEffect } from "react";


export default function App() {
useEffect(() => {
console.log("useEffect Statements");
}, []);


useLayoutEffect(() => {
console.log("useLayoutEffect Statements");
}, []);
return (
<div>
<h1>Hello Guys</h1>
</div>
);
}
 

__abc1 __abc0 __abc2

这里真正的问题是你需要改变“组件生命周期思维”;转变为一种“使用效果的心态”;

一个React组件仍然是一个javascript函数,所以,如果你想让某件事在另一件事之前执行,你必须简单地从上到下执行它,如果你认为它是一个函数,它仍然是一个函数,例如:

const myFunction = () => console.log('a')
const mySecondFunction = () => console.log('b)


mySecondFunction()
myFunction()


/* Result:
'b'
'a'
*/

这真的很简单,不是吗?

const MyComponent = () => {
const someCleverFunction = () => {...}


someCleverFunction() /* there I can execute it BEFORE
the first render (componentWillMount)*/


useEffect(()=> {
someCleverFunction() /* there I can execute it AFTER the first render */
},[]) /*I lie to react saying "hey, there are not external data (dependencies) that needs to be mapped here, trust me, I will leave this in blank.*/


return (
<div>
<h1>Hi!</h1>
</div>
)}

在这个特定的例子中,它是正确的。但是如果我这样做会发生什么:

const MyComponent = () => {
const someCleverFunction = () => {...}


someCleverFunction() /* there I can execute it BEFORE
the first render (componentWillMount)*/


useEffect(()=> {
someCleverFunction() /* there I can execute it AFTER the first render */
},[]) /*I lie to react saying "hey, there are not external data (dependencies) that needs to be maped here, trust me, I will leave this in blank.*/
return (
<div>
<h1>Hi!</h1>
</div>
)}
< p >这“cleverFunction"我们在组件的每一次重渲染中定义的都不一样。 这将导致一些严重的错误,在某些情况下会导致不必要的组件重渲染或无限的重渲染循环

真正的问题是React函数组件是一个“自我执行”的函数。由于使用了useEffect钩子(以及其他钩子),根据您的状态可以多次使用。

简而言之,useEffect是一个钩子,专门设计用于同步你的数据与你在屏幕上看到的任何东西。如果你的数据改变了,你的useEffect钩子需要知道这一点,总是。这包括你的方法,对于是数组依赖项。 如果没有定义,就会出现难以发现的bug

正因为如此,了解它是如何工作的,以及你可以做什么来得到你想要的“反应”是很重要的。道路


const initialState = {
count: 0,
step: 1,
done: false
};


function reducer(state, action) {
const { count, step } = state;
if (action.type === 'doSomething') {
if(state.done === true) return state;
return { ...state, count: state.count + state.step, state.done:true };
} else if (action.type === 'step') {
return { ...state, step: action.step };
} else {
throw new Error();
}
}


const MyComponent = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;


useEffect(() => {
dispatch({ type: 'doSomething' });
}, [dispatch]);


return (
<div>
<h1>Hi!</h1>
</div>
)}

useReducer的分派方法是静态的,这意味着无论你的组件被重新渲染多少次,它都是相同的方法。因此,如果你只想执行某件事一次,而且你想在组件挂载之后立即执行,你可以像上面的例子那样做。这是一种正确的表述方式。

来源:使用效果的完整指南-丹阿布拉莫夫

话虽如此,如果你喜欢尝试事物,并想知道如何做到这一点,“势在必行”;你可以使用带有计数器或布尔值的useRef()来检查该ref是否存储了已定义的引用,这是一个必要的方法,如果你不熟悉react在窗帘后面发生的事情,建议避免使用它。

这是因为useRef()是一个钩子,它保存传递给它的参数,而不管呈现的数量(我保持简单,因为这不是这里问题的焦点,你可以阅读关于useRef的神奇的文章)。所以这是知道组件第一次渲染发生的最佳方法。

我留下了一个例子,展示了同步“outside”的3种不同方式;效果(像一个外部函数)与"inner"组件状态。

您可以在这里运行这个代码片段以查看日志并了解这3个函数何时执行。

const { useRef, useState, useEffect, useCallback } = React


// External functions outside react component (like a data fetch)


function renderOnce(count) {
console.log(`renderOnce: I executed ${count} times because my default state is: undefined by default!`);
}


function renderOnFirstReRender(count) {
console.log(`renderOnUpdate: I executed just ${count} times!`);
}


function renderOnEveryUpdate(count) {
console.log(`renderOnEveryUpdate: I executed ${count ? count + 1 : 1} times!`);
}


const MyComponent = () => {
const [count, setCount] = useState(undefined);


const mounted = useRef(0);


// useCallback is used just to avoid warnings in console.log
const renderOnEveryUpdateCallBack = useCallback(count => {
renderOnEveryUpdate(count);
}, []);


if (mounted.current === 0) {
renderOnce(count);
}


if (mounted.current === 1) renderOnFirstReRender(count);


useEffect(() => {
mounted.current = mounted.current + 1;
renderOnEveryUpdateCallBack(count);
}, [count, renderOnEveryUpdateCallBack]);


return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(prevState => (prevState ? prevState + 1 : 1))}>TouchMe</button>
</div>
);
};


class App extends React.Component {
render() {
return (
<div>
<h1>hI!</h1>
</div>
);
}
}


ReactDOM.createRoot(
document.getElementById("root")
).render(
<MyComponent/>
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

如果你执行它,你会看到如下内容: result of the snippet < / p >