简体   繁体   English

如何在 React 中渲染之前触发 useEffects?

[英]How to trigger useEffects before render in React?

I have a prop being passed from a parent component to a child component which changes based on the user's input.我有一个从父组件传递到子组件的道具,该道具会根据用户的输入而变化。

I want to trigger a data fetch in the child component when that prop changes before the child component is rendered.我想在子组件渲染之前该道具发生变化时触发子组件中的数据获取。 How can I do it?我该怎么做?

I tried in the following manner by using useEffects(()=>{},[props.a, props.b]) but that is always called after the render.我通过使用useEffects(()=>{},[props.a, props.b])尝试了以下方式,但这总是在渲染之后调用。 Please help!请帮忙!

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function parentComponent() {
  const [inputs, setInputs] = useState({ a: "", b: "" });
  return (
    <>
      <input
        value={inputs.a}
        onChange={(event) => {
          const value = event.target.value;
          setInputs((prevState) => {
            return { ...prevState, a: value };
          });
        }}
      />
      <input
        value={inputs.b}
        onChange={(event) => {
          const value = event.target.value;
          setInputs((prevState) => {
            return { ...prevState, b: value };
          });
        }}
      />
      <ChildComponent a={inputs.a} b={inputs.b} />
    </>
  );
}

function ChildComponent(props) {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState({});

  useEffect(() => {
    console.log("updating new data based on props.a: " + props.a);
    setData({ name: "john " + props.a });
    return () => {};
  }, [props.a, props.b]);

  useEffect(() => {
    console.log("data successfully changed");
    console.log(data);
    if (Object.keys(data).length !== 0) {
      setIsLoading(false);
    }
    return () => {};
  }, [data]);

  function renderPartOfComponent() {
    console.log("rendering POC with props.a: " + props.a);
    return <div>data is: {data.name}</div>;
  }
  return (
    <div className="App">{isLoading ? null : renderPartOfComponent()}</div>
  );
}

In the console what I get is:在控制台中,我得到的是:

rendering POC with props.a: fe 
rendering POC with props.a: fe 
updating new data based on props.a: fe 
rendering POC with props.a: fe 
rendering POC with props.a: fe 
data successfully changed 
Object {name: "john fe"}
rendering POC with props.a: fe 
rendering POC with props.a: fe 

If you know how I can make the code more efficient, that would be a great help as well!如果您知道我如何使代码更高效,那也将是一个很大的帮助!

Here's the codesandbox link for the code: https://codesandbox.io/s/determined-northcutt-6z9f8?file=/src/App.js:0-1466这是代码的代码框链接: https ://codesandbox.io/s/determined-northcutt-6z9f8?file=/src/App.js:0-1466

I found the solution by creating and maintaining state within the ChildComponent我通过在 ChildComponent 中创建和维护状态找到了解决方案

So, the order of processes was this: props modified -> render takes place -> useEffect block is executed.所以,进程的顺序是这样的:修改道具 -> 渲染发生 -> 执行 useEffect 块。

I found the workaround by simply instantiating a state within the childComponent and making sure that the props state is the same as the one in the child component before rendering, else it would just show loading... This works perfectly.我通过简单地在 childComponent 中实例化一个状态并确保 props 状态与渲染之前的子组件中的状态相同来找到解决方法,否则它只会显示加载......这非常有效。

useEffect is always called after the render phase of the component. useEffect总是在组件的渲染阶段之后调用。 This is to avoid any side-effects from happening during the render commit phase (as it'd cause the component to become highly inconsistent and keep trying to render itself).这是为了避免在渲染提交阶段发生任何副作用(因为它会导致组件变得高度不一致并继续尝试渲染自身)。

  • Your ParentComponent consists of Input, Input & ChildComponent.您的ParentComponentInput、Input 和 ChildComponent 组成。
  • As you type in textbox, ParentComponent: inputs state is modified.当您在文本框中键入时, ParentComponent: inputs状态被修改。
  • This state change causes ChildComponent to re-render, hence renderPartOfComponent is called (as isLoading remains false from previous render).此状态更改导致 ChildComponent 重新渲染,因此调用了renderPartOfComponent (因为 isLoading 在之前的渲染中保持为假)。
  • After re-render, useEffect will be invoked (Parent's state propagates to Child).重新渲染后,将调用 useEffect(Parent 的状态传播到 Child)。
  • Since isLoading state is modified from the effects, another rendering happens.由于isLoading状态是从效果中修改的,因此会发生另一次渲染。

You can use function below:您可以使用以下功能:

// utils.js
const useBeforeRender = (callback, deps) => {
    const [isRun, setIsRun] = useState(false);

    if (!isRun) {
        callback();
        setIsRun(true);
    }

    useEffect(() => () => setIsRun(false), deps);
};

// yourComponent.js
useBeforeRender(() => someFunc(), []);

Try this尝试这个

You can use useMemo , which doesn't wait for re-render .您可以使用useMemo ,它不等待re-render It will execute as long as the dependencies are changed.只要依赖关系发生变化,它就会执行。

useMemo(()=>{
    doSomething() //Doesn't want until render is completed 
}, [dep1, dep2])

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

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