首頁  >  問答  >  主體

作為 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粉685757239381 天前482

全部回覆(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
  • 取消回覆