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

Nam*_*anD 26 javascript frontend reactjs react-hooks use-effect

我有一个道具从父组件传递到子组件,该组件根据用户的输入而变化。

当子组件渲染之前该 prop 发生变化时,我想在子组件中触发数据获取。我该怎么做?

我通过使用尝试了以下方式useEffects(()=>{},[props.a, props.b]),但总是在渲染后调用。请帮忙!

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

Run Code Online (Sandbox Code Playgroud)

在控制台中我得到的是:

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 
Run Code Online (Sandbox Code Playgroud)

如果您知道我如何使代码更高效,那也会有很大的帮助!

这是代码的codesandbox链接:https://codesandbox.io/s/dependent-northcutt-6z9f8 ?file=/src/App.js:0-1466

Abr*_*ham 26

解决方案

您可以使用useMemo,它不会等待re-render. 只要依赖项发生更改,它就会执行。

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

  • 这才是正确的做法! (4认同)

enr*_*r99 11

现在,您可以使用在浏览器重新绘制屏幕之前触发useLayoutEffect的版本。useEffect

文档: https: //beta.reactjs.org/reference/react/useLayoutEffect


Joh*_*tin 7

您可以使用以下功能:

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

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

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

// yourComponent.js
useBeforeRender(() => someFunc(), []);
Run Code Online (Sandbox Code Playgroud)

  • 好吧,你可以只使用`useMemo`。这与您的自定义挂钩完全一样,但还支持返回值。 (9认同)
  • 您能详细说明一下吗? (2认同)

Abh*_*yer 6

useEffect始终在组件的渲染阶段之后调用。这是为了避免在渲染提交阶段发生任何副作用(因为它会导致组件变得高度不一致并继续尝试渲染自身)。

  • 您的ParentComponent由输入、输入和子组件组成
  • 当您在文本框中键入内容时,ParentComponent: inputs状态会被修改。
  • 此状态更改会导致 ChildComponent 重新渲染,因此调用renderPartOfComponent (因为 isLoading 在之前的渲染中仍然为 false)。
  • 重新渲染后,将调用 useEffect(Parent 的状态传播到 Child)。
  • 由于isLoading状态是根据效果修改的,因此会发生另一次渲染。


Nam*_*anD 3

我通过在 ChildComponent 中创建和维护状态找到了解决方案

因此,处理的顺序是这样的:props 修改 -> 渲染发生 -> useEffect 块被执行。

我找到了解决方法,只需在 childComponent 中实例化一个状态,并确保 props 状态与渲染前子组件中的状态相同,否则它只会显示正在加载......这非常有效。

  • 如果您愿意展示一个简单的代码演示那就太好了 (23认同)