如果方法依赖于 React 中的状态变量,如何将方法传递给 child 元素

How to pass a method into a child element if the method relies on state variables in React

过去几天我一直在学习 React,在大多数情况下它是有道理的,但是有一件事让我很困惑。

如果我有

每次我尝试这样做时,似乎 child 元素正在调用一些旧版本的 parent 元素(可能是实际创建的 parent 的实例child) 而不是当前版本。

我感觉我想要的东西在基本层面上是错误的,而不是 React 方式

我尝试这样做的原因是我的主要parent包含17个div,每个代表乐器上的一个键,每个至少包含20-30 div 秒。最低的 div(其中至少有几百个)有一个 onClick 事件,我想根据是否按住修改键(shift、control 等)来修改它的功能。

目前我已经shiftPressed 的状态 提升到单个 parent 元素上,然后将其值传递给每个 child 通过 props,然而 re-rendering 数百 div 每当用户按下 shift 需要相当长的时间。

我做了一个代码沙箱来显示当前的问题sandbox

沙盒代码:

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

export default function App() {
  //Our state holding data
  const [state, setState] = useState(false);

  //Our state holding the view
  const [view, setView] = useState(<div></div>);

  const printState = useRef(null);

  //Component did mount hook
  useEffect(() => {
    reGenerate();
  }, []);

  //state update hook
  useEffect(() => {
    printState.current();
  }, [state]);

  //function to flip the state
  const flipState = () => {
    setState(!state);
  };

  //The method that updates the view
  //(The idea being that I don't want to update the view on every state change)
  const reGenerate = () => {
    setView(
      <>
        <p>
          State: {state && "true"} {state || "false"}
        </p>
        <Child callback={printState} />
      </>
    );
  };

  //Method for validation
  printState.current = () => {
    console.log("Printed state: " + state);
  };
  
  return (
    <div className="App">
      <h1>Parent-child-prop-problem (prop-lem)</h1>
      <ol>
        <li>click "force regeneration"</li>
        <li>
          click "flip state" and the value of state after the flip will be
          printed in console, but it won't show up on the HTML element
        </li>
        <li>
          Click "print state (from child)" and observe that the console is
          printing the old version of the state
        </li>
      </ol>
      <button onClick={flipState}>Flip State</button>
      <button onClick={reGenerate}>Force Regeneration</button>
      {view}
    </div>
  );
}

function Child(props) {
  return (
    <div>
      <button onClick={props.callback.current}>Print State (from child)</button>
    </div>
  );
}

快速浏览一下您的沙箱代码,我发现您正在以状态存储 JSX,这是 anti-pattern 并且通常会导致像您描述的那样陈旧的外壳。

I don't want to re-create the view object every time any state changes

“重新创建”视图是在 React 中渲染 UI 作为状态或道具更新的必要步骤。状态应该只存储数据并且 UI 应该从 呈现 状态。换句话说,将您的 UI 视为状态和道具的函数。切换 state 状态值并从状态渲染 UI。

示例:

export default function App() {
  //Our state holding data
  const [state, setState] = useState(false);

  const printState = useRef(null);

  //state update hook
  useEffect(() => {
    printState.current();
  }, [state]);

  //function to flip the state
  const flipState = () => {
    setState(!state);
  };

  //Method for validation
  printState.current = () => {
    console.log("Printed state: " + state);
  };

  return (
    <div className="App">
      <h1>Parent-child-prop-problem (prop-lem)</h1>
      <ol>
        <li>
          click "flip state" and the value of state after the flip will be
          printed in console, but it won't show up on the HTML element
        </li>
        <li>
          Click "print state (from child)" and observe that the console is
          printing the old version of the state
        </li>
      </ol>
      <button onClick={flipState}>Flip State</button>
      <p>State: {state ? "true" : "false"}</p>
      <Child callback={printState} />
    </div>
  );
}

function Child(props) {
  return (
    <div>
      <button onClick={props.callback.current}>Print State (from child)</button>
    </div>
  );
}

通常还认为 anti-pattern 使用任何类型的“forceUpdate”函数,这是一种代码味道。在几乎所有情况下,如果你遇到需要强制 React 重新渲染的点,你就做错了。这是您退后一步并跟踪代码以查找某个状态或某些道具未正确更新以自然触发重新渲染的时间。