首页  >  问答  >  正文

作为 prop 传递的数组不反映更改

这个问题更多的是为了更多地了解反应如何处理和对变化做出反应,而不是实现,因此我让 immutable-props-apprach 稍微发展一下。

我试图获取数组的第一个元素并将其从原始数组中删除,该数组作为道具传递给组件:

const ChildComponent = (
  arrayToChange: Array<any>[]
) => {
  const elementFromArray = arrayToChange.shift();
}

从shift()方法的定义来看::

shift() 方法从数组中删除第一个元素并返回该删除的元素。此方法更改数组的长度。

尽管 elementFromArray 变量现在包含数组中的元素,但数组是完整的,它没有受到任何影响,仍然包含所有元素。

但这怎么可能呢? React 应该通过引用传递 props,因此原始数组应该受到影响。如果 React 采取了一些保护措施,并且这些更改不会反映在父级中,我会理解,但是,我仍然希望更改能够反映在子级中。 我找不到任何有用的东西来解释这种行为,大多数资源只提到了 props 的不可变方法以及如何找到解决方法,而不是其背后的原因或逻辑。

尽管 elementFromArray 变量现在包含数组中的元素,但数组是完整的,它没有受到任何影响,并且仍然包含所有元素。但是,如果我使用 Push() 方法,则会反映更改,arrayToChange 会多包含一个元素。

我的问题是 - 为什么 arrayToChange 对这些方法的反应不同?如果shift()不改变内容,我希望push()也不会。

P粉685757239P粉685757239432 天前526

全部回复(2)我来回复

  • P粉182218860

    P粉1822188602023-09-07 14:26:37

    function Parent() {
      const forceUpdate = useForceUpdate();
      const [letters] = React.useState(["a", "b", "c"]);
    
      return (
        <div className="bg-yellow">
          A: {JSON.stringify(letters)}
          <ChildShowArray array={letters} />
          B: {JSON.stringify(letters)}
          <ChildChangeArray arrayToChange={letters} />
          C: {JSON.stringify(letters)}
          <ChildShowArray array={letters} />
          D: {JSON.stringify(letters)}
    
          <hr />
          <button type="button" onClick={forceUpdate}>re-render</button>
        </div>
      );
    }
    
    function ChildChangeArray({ arrayToChange }) {
      const elementFromArray = arrayToChange.shift();
      
      return (
        <div className="bg-red">
          elementFromArray = {JSON.stringify(elementFromArray)}
        </div>
      );
    }
    
    function ChildShowArray({ array }) {
      return (
        <div className="bg-green">
          array = {JSON.stringify(array)}
        </div>
      );
    }
    
    // helper hook
    function useForceUpdate() {
      const [_state, setState] = React.useState({});
      return React.useCallback(() => { setState({}) }, []);
    }
    
    ReactDOM.createRoot(document.querySelector("#root")).render(<Parent />)
    .bg-yellow { background: yellow }
    .bg-red { background: red }
    .bg-green { background: green }
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div id="root"></div>

    如果您将渲染过程视为广度优先算法,则可以解释代码片段中的行为。

    JSX 将转换:

    <div className="bg-yellow">
      A: {JSON.stringify(letters)}
      <ChildShowArray array={letters} />
      B: {JSON.stringify(letters)}
      <ChildChangeArray arrayToChange={letters} />
      C: {JSON.stringify(letters)}
      <ChildShowArray array={letters} />
      D: {JSON.stringify(letters)}
    </div>

    进入以下 JavaScript:

    React.createElement("div", { className: "bg-yellow" },
      "A: ", JSON.stringify(letters),
      React.createElement(ChildShowArray, { array: letters }),
      "B: ", JSON.stringify(letters),
      React.createElement(ChildChangeArray, { arrayToChange: letters }),
      "C: ", JSON.stringify(letters),
      React.createElement(ChildShowArray, { array: letters }),
      "D: ", JSON.stringify(letters),
    )
    

    React.createElement(ChildShowArray, { array: letter }) 创建一个不会立即调用 ChildShowArray 组件的结构。它将创建某种中间结构/对象,仅在渲染器要求它运行时运行。

    放置在 {...} (JSX 上下文)中的 JavaScript 直接作为参数传递,因此直接解析。这意味着 Parent 中的所有 {JSON.stringify(letters)} 在子组件中的任何代码运行之前运行。

    当构建父结构完成后,渲染器将访问每个中间结构/对象并要求其渲染。这是从上到下完成的,这就是为什么第一个 ChildShowArray 渲染仍然显示完整数组的原因。然后渲染 ChildChangeArray 并删除第一个元素。第二个 ChildShowArray 渲染反映了此更改,并且在没有第一个元素的情况下进行渲染。

    请注意,shift() 确实会更改 letters 的内容,但是当调用它时,Parent 的内容已经呈现并且不再变化。此更改确实会影响下一次渲染的Parent(单击代码片段中的“重新渲染”按钮)。它还会影响其下方使用相同数组引用的其他子组件的渲染。

    回复
    0
  • P粉287726308

    P粉2877263082023-09-07 00:57:49

    我不完全确定这个问题是什么,但我在这里冒险猜测一下,所以请在这里发表评论,我会在投票之前进行更改。

    我认为你应该在你的子组件中尝试这个:

    const [data, setData] = React.useState(arrayToChange);
    
    React.useEffect(() => setData(arrayToChange), [arrayToChange.length]);

    然后使用“data”映射到 jsx 中的输出

    然后在父组件中,对 arrayToChange 进行移位。您可以将 useEffect 视为一个“观察者”,当数组长度发生变化时它将触发。

    回复
    0
  • 取消回复