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

ki1q1bka  于 2023-06-28  发布在  Java
关注(0)|答案(2)|浏览(101)

这个问题更多的是为了理解react如何处理和响应变化,而不是实现,因此我让immutable-props-apprach稍微多走一点。
我试图获取数组的第一个元素,并将其从原始数组中删除,该数组作为prop传递给component:

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

关于definition of the shift() method:
shift()方法从数组中删除第一个元素并返回该删除的元素。此方法更改数组的长度。
尽管elementFromArray变量现在包含了array中的元素,但数组是完整的,它没有受到任何影响,仍然包含所有元素。
但这怎么可能?React应该通过引用传递props,因此原始数组应该受到影响。如果React有一些保护措施,并且这些更改不会反映在父级中,我会理解的,但是,我仍然希望这些更改会反映在子级中。我找不到任何有用的东西来解释这种行为,大多数资源只提到了 prop 的不可变方法以及如何找到一种方法,而不是背后的原因或逻辑。
尽管elementFromArray变量现在包含了array中的元素,但数组是完整的,它没有受到任何影响,仍然包含所有元素。但是,如果我使用push()方法,那么这些变化就会被反映出来,arrayToChange多包含一个元素。
那么我的问题是--为什么arrayToChange对这些方法的React不同?如果shift()不改变内容,我希望push()也不会。

chhqkbe1

chhqkbe11#

我不完全确定这个问题是什么,但我在这里要冒险猜测一下,所以请在这里发表评论,我会在否决之前改变。
我认为你应该在你的子组件中试试这个:

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

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

然后使用“data”Map到JSX中的输出
然后在父组件中,对arrayToChange进行移位。您可以将useEffect视为一个“观察者”,它将在数组长度发生变化时触发。

50pmv0ei

50pmv0ei2#

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: letters })创建一个结构,它不会立即调用ChildShowArray组件。它将创建某种中间结构/对象,仅在渲染器要求它运行时才运行。
放置在{...}(JSX上下文)中的JavaScript直接作为参数传递,因此直接解析。这意味着Parent中的所有{JSON.stringify(letters)}都在子组件中的任何代码运行之前运行。
当构建父结构完成时,渲染器将访问每个中间结构/对象并要求其渲染。这是从上到下完成的,这就是为什么第一个ChildShowArray渲染仍然显示完整的数组。然后渲染ChildChangeArray,删除第一个元素。第二个ChildShowArray呈现反映了这一变化,并且在没有第一个元素的情况下呈现。
请注意,shift()确实会更改letters的内容,但当调用它时,Parent的内容已经呈现,不再更改。此更改会影响下一次渲染时的Parent(单击代码片段中的“重新渲染”按钮)。它还会影响其下使用相同数组引用的其他子组件的渲染。

相关问题