简体   繁体   English

React 函数/Hooks 组件上的 componentDidMount 等价物?

[英]componentDidMount equivalent on a React function/Hooks component?

Are there ways to simulate componentDidMount in React functional components via hooks?有没有办法通过钩子模拟 React 功能componentDidMount中的 componentDidMount?

For the stable version of hooks (React Version 16.8.0+)对于钩子的稳定版本(React 版本 16.8.0+)

For componentDidMount对于componentDidMount

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

For componentDidUpdate对于componentDidUpdate

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

For componentWillUnmount对于componentWillUnmount

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

So in this situation, you need to pass your dependency into this array.所以在这种情况下,你需要将你的依赖传递到这个数组中。 Let's assume you have a state like this假设您有这样的 state

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

And whenever count increases you want to re-render your function component.并且每当计数增加时,您都希望重新渲染 function 组件。 Then your useEffect should look like this那么你的useEffect应该是这样的

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

This way whenever your count updates your component will re-render.这样,每当您的计数更新时,您的组件就会重新渲染。 Hopefully this will help a bit.希望这会有所帮助。

There is no exact equivalent for componentDidMount in react hooks. react hooks 中没有与componentDidMount完全相同的等效项。


In my experience, react hooks requires a different mindset when developing it and generally speaking you should not compare it to the class methods like componentDidMount .根据我的经验,react hooks 在开发时需要不同的思维方式,一般来说,您不应将其与 class 方法(如componentDidMount )进行比较。

With that said, there are ways in which you can use hooks to produce a similar effect to componentDidMount .话虽如此,您可以通过多种方式使用钩子来产生与componentDidMount类似的效果。

Solution 1:解决方案1:

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

Solution 2:解决方案2:

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

Solution 3 (With function):解决方案3(带功能):

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

Solution 4 (useCallback):解决方案4(useCallback):

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

Solution 5 (Getting creative):解决方案 5(发挥创意):

 export default function useDidMountHook(callback) { const didMount = useRef(null) useEffect(() => { if (callback &&.didMount.current) { didMount current = true callback() } }) }

It is worth noting that solution 5 should only really be used if none of the other solutions work for your use case .值得注意的是,只有当其他解决方案都不适合您的用例时,才应真正使用解决方案 5 If you do decide you need solution 5 then I recommend using this pre-made hook use-did-mount .如果您确实决定需要解决方案 5,那么我建议您使用这个预制的钩子 use-did-mount

Source (With more detail): Using componentDidMount in react hooks来源(更详细): 在反应钩子中使用 componentDidMount

There's no componentDidMount on functional components, but React Hooks provide a way you can emulate the behavior by using the useEffect hook.功能组件上没有componentDidMount ,但是 React Hooks 提供了一种可以使用useEffect挂钩来模拟行为的方法。

Pass an empty array as the second argument to useEffect() to run only the callback on mount only.将一个空数组作为第二个参数传递给useEffect()以仅在挂载时运行回调。

Please read the documentation on 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>

useEffect() hook allows us to achieve the functionality of componentDidMount, componentDidUpdate componentWillUnMount functionalities. useEffect()钩子允许我们实现componentDidMount的功能,componentDidUpdate componentWillUnMount的功能。

Different syntaxes of useEffect() allows to achieve each of the above methods. useEffect() 的不同语法允许实现上述每个方法。

i) componentDidMount i) componentDidMount

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

ii) componentDidUpdate ii) componentDidUpdate

 useEffect(() => { //code here }, [x,y,z]); //where x,y,z are state variables on whose update, this method should get triggered

iii) componentDidUnmount iii) componentDidUnmount

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

You can check the official react site for more info.您可以查看官方反应站点以获取更多信息。 Official React Page on Hooks Hooks 官方 React 页面

Although accepted answer works, it is not recommended.尽管接受的答案有效,但不建议这样做。 When you have more than one state and you use it with useEffect, it will give you warning about adding it to dependency array or not using it at all.当您拥有多个 state 并将其与 useEffect 一起使用时,它会警告您将其添加到依赖数组或根本不使用它。

It sometimes causes the problem which might give you unpredictable output.它有时会导致问题,可能会给您带来不可预测的 output。 So I suggest that you take a little effort to rewrite your function as class.所以我建议你花点力气把你的 function 重写为 class。 There are very little changes, and you can have some components as class and some as function.变化很小,您可以将一些组件作为 class 和一些作为 function。 You're not obligated to use only one convention.您没有义务只使用一种约定。

Take this for example以此为例

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(...); }

and

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(...); }

This is only for example .这只是举例 so lets not talk about best practices or potential issues with the code.所以让我们不要谈论代码的最佳实践或潜在问题。 Both of this has same logic but the later only works as expected.这两者具有相同的逻辑,但后者只能按预期工作。 You might get componentDidMount functionality with useEffect running for this time, but as your app grows, there are chances that you MAY face some issues.这次你可能会通过 useEffect 获得 componentDidMount 功能,但是随着你的应用程序的增长,你可能会遇到一些问题。 So, rather than rewriting at that phase, it's better to do this at early stage.因此,与其在那个阶段重写,不如在早期阶段这样做。

Besides, OOP is not that bad, if Procedure-Oriented Programming was enough, we would never have had Object-Oriented Programming.此外,OOP 还不错,如果面向过程编程足够了,我们永远不会有面向对象编程。 It's painful sometimes, but better (technically. personal issues aside).有时会很痛苦,但更好(技术上。个人问题除外)。

ComponentDidMount ComponentDidMount

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

Yes, life cycle methods like componentDidMount are only available in class components. 是的,componentDidMount等生命周期方法仅在类组件中可用。

Class components should not be considered deprecated, even after the advent of hooks, they will remain a substantial part of React. 类组件不应被视为已弃用,即使在钩子出现后,它们仍将是React的重要组成部分。

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

Please visit this official docs .请访问此官方文档 Very easy to understand the latest way .很容易理解最新的方法

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

Info about async functions inside the hook:关于钩子内的异步函数的信息:

Effect callbacks are synchronous to prevent race conditions.效果回调是同步的以防止竞争条件。 Put the async function inside:将异步 function 放入:

 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

useLayoutEffect hook is the best alternative to ComponentDidMount in React Hooks. useLayoutEffect hook 是 React Hooks 中ComponentDidMount的最佳替代方案。

useLayoutEffect hook executes before Rendering UI and useEffect hook executes after rendering UI. useLayoutEffect钩子在渲染 UI 之前执行,而useEffect钩子在渲染 UI 之后执行。 Use it depend on your needs.根据您的需要使用它。

Sample Code:示例代码:

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

You want to use useEffect() , which, depending on how you use the function, can act just like componentDidMount().您想使用useEffect() ,这取决于您使用 function 的方式,其作用类似于 componentDidMount()。

Eg.例如。 you could use a custom loaded state property which is initially set to false, and switch it to true on render, and only fire the effect when this value changes.您可以使用自定义loaded的 state 属性,该属性最初设置为 false,并在渲染时将其切换为 true,并且仅在此值更改时触发效果。

Documentation文档

Yes, there is a way to SIMULATE a componentDidMount in a React functional component是的,有一种方法可以在 React 功能组件中模拟componentDidMount

DISCLAIMER: The real problem here is that you need to change from "component life cycle mindset" to a "mindset of useEffect"免责声明:这里真正的问题是您需要从“组件生命周期心态”转变为“useEffect 心态”

A React component is still a javascript function, so, if you want something to be executed BEFORE some other thing you must simply need to execute it first from top to bottom, if you think about it a function it's still a funtion like for example: React 组件仍然是 javascript function,因此,如果您想在执行其他操作之前先执行某些操作,则必须先从上到下执行它

const myFunction = () => console.log('a') const mySecondFunction = () => console.log('b) mySecondFunction() myFunction() /* Result: 'b' 'a' */

That is really simple isn't it?这真的很简单不是吗?

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

And in this specific case it's true.在这种特定情况下,这是真的。 But what happens if I do something like that:但是如果我这样做会发生什么:

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

This "cleverFunction" we are defining it's not the same in every re-render of the component.我们定义的这个“cleverFunction”在组件的每次重新渲染中都不相同。 This lead to some nasty bugs and, in some cases to unnecessary re-renders of components or infinite re-render loops.这会导致一些讨厌的错误,并且在某些情况下会导致不必要的组件重新渲染或无限的重新渲染循环。

The real problem with that is that a React functional component is a function that "executes itself" several times depending on your state thanks to the useEffect hook (among others).真正的问题是,React 功能组件是 function,由于 useEffect 钩子(等等),它会根据您的 state 多次“执行自身”。

In short useEffect it's a hook designed specifically to synchronize your data with whatever you are seeing on the screen.简而言之 useEffect 它是一个专门设计用于将您的数据与您在屏幕上看到的任何内容同步的钩子。 If your data changes, your useEffect hook needs to be aware of that, always .如果您的数据发生更改,您的 useEffect 挂钩需要始终注意这一点。 That includes your methods, for that it's the array dependencies.这包括您的方法,因为它是数组依赖项。 Leaving that undefined leaves you open to hard-to-find bugs.留下未定义的内容会使您容易发现难以发现的错误。

Because of that it's important to know how this work, and what you can do to get what you want in the "react" way.因此,重要的是要知道它是如何工作的,以及你可以做些什么来以“反应”的方式获得你想要的东西。

 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's dispatch method it's static so it means it will be the same method no matter the amount of times your component is re-rendered. useReducer 的调度方法是 static 所以这意味着无论你的组件被重新渲染多少次,它都是相同的方法。 So if you want to execute something just once and you want it rigth after the component is mounted, you can do something like the above example.因此,如果您只想执行一次并且希望在安装组件后立即执行,您可以执行类似上述示例的操作。 This is a declarative way of do it right.这是一种正确的声明方式。

Source: The Complete Guide to useEffect - By Dan Abramov资料来源: useEffect 完整指南 - Dan Abramov

That being said if you like to experiment with things and want to know how to do it "the imperative wat" you can use a useRef() with a counter or a boolean to check if that ref stores a defined reference or not, this is an imperative approach and it's recommended to avoid it if you're not familiar with what happen with react behind curtains.话虽这么说,如果您喜欢尝试事物并想知道如何做“命令式 wat”,您可以使用带有计数器的useRef()或 boolean 来检查该 ref 是否存储定义的引用,这是一种命令式的方法,如果您不熟悉幕后反应会发生什么,建议您避免使用它。

That is because useRef() is a hook that saves the argument passed to it regardless of the amount of renders (I am keeping it simple because it's not the focus of the problem here, you can read this amazing article about useRef ).那是因为 useRef() 是一个钩子,它保存传递给它的参数,而不管渲染的数量(我保持简单,因为它不是这里问题的重点,你可以阅读这篇关于 useRef的惊人文章)。 So it's the best approach to known when the first render of the component happened.因此,这是了解组件第一次渲染发生时间的最佳方法。

I leave an example showing 3 different ways of synchronise an "outside" effect (like an external function) with the "inner" component state.我留下了一个例子,展示了 3 种不同的方式将“外部”效果(如外部函数)与“内部”组件 state 同步。

You can run this snippet right here to see the logs and understand when these 3 functions are executed.您可以在此处运行此代码段以查看日志并了解这 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>

If you execute it you will see something like this:如果你执行它,你会看到这样的东西: 片段的结果

the exact equivalent hook for componentDidMount() is componentDidMount() 的确切等效钩子是

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

hope this helpful:)希望这有帮助:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM