React列出的对象在状态更改时卸载。

huangapple go评论66阅读模式
英文:

React listed objects unmount on state change

问题

I have translated the code part for you. If you need further assistance or have any questions, feel free to ask.

App.tsx:

import { useState } from 'react'
import Number from './Number'
import './App.css'

function App() {
    const [list, setList] = useState(["1", "2", "3", "4"]);
    const [x, setX] = useState({});

    function ComponentList() {
        return (
            <>
                {list.map(i => <Number num={i} key={i}/>) }
            </>
        )
    }

    return (
        <div className="App">
            <ComponentList/>
            <div onClick={()=>{
                setX({});
            }}>Update</div>
        </div>
    )
}

export default App

Number.tsx:

import React, { useState } from 'react';

type props = {
    num: string
}

function Number({num}: props) {
    const [color, setColor] = useState("rgb(0, 0, 0)");

    return (
        <div style={{color: color}} onClick={()=>{
            setColor("rgb(255, 255, 255)");
        }}>
            {num}
        </div>
    )
}

export default Number

If you have any specific questions or need assistance with the issue you described, please let me know.

英文:

I have two components in my react (ts) app.

App.tsx:

import { useState } from &#39;react&#39;
import Number from &#39;./Number&#39;
import &#39;./App.css&#39;

function App() {
    const [list, setList] = useState([&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]);
    const [x, setX] = useState({});

    function ComponentList(){
        return(&lt;&gt;
            {list.map(i =&gt; &lt;Number num={i} key={i}/&gt;) }
        &lt;/&gt;)
    }

    return (
        &lt;div className=&quot;App&quot;&gt;
            &lt;ComponentList/&gt;
            &lt;div onClick={()=&gt;{
                setX({});
            }}&gt;Update&lt;/div&gt;
        &lt;/div&gt;
    )
}

export default App

Number.tsx:

import React, {useState} from &#39;react&#39;

type props = {
    num: string
}

function Number({num}:props) {
    const [color, setColor] = useState(&quot;rgb(0, 0, 0)&quot;);

    return (
        &lt;div style={{color: color}} onClick={()=&gt;{
            setColor(&quot;rgb(255, 255, 255)&quot;);
        }}&gt;
            {num}
        &lt;/div&gt;
    )
}

export default Number

Here's the problem:
When any state on the parent component (App.tsx) changes, the Number components in the list re-mount, resetting all their states to initial value. How can I get rid of this problem while keeping the list component?

The issue is very simple to re-create. When you use these two components in a react app and click the numbers to change their color, the color is reset upon clicking the "update" button, which seems illogical to me since the "setX" call doesn't change anything about the list.

答案1

得分: 1

You've defined ComponentList inside of App. This isn't supported. Every time App rerenders, you create a brand new definition for ComponentList. Its code may have the same text as the previous one, but it's a different function, so a different component type as far as React is concerned. So React has to unmount the old ones and mount the new ones.

The fix is to move the component out. You'll need to change list to be a prop instead of a closure variable.

function App() {
  const [list, setList] = useState(["1", "2", "3", "4"]);
  // ...
  <ComponentList list={list}/>
  // ...
}

function ComponentList({ list }){
  return(<>
    {list.map(i => <Number num={i} key={i}/>) }
  </>)
}
英文:
function App() {
    // ...

    function ComponentList(){
        // ...
    }
}

You've defined ComponentList inside of App. This isn't supported. Every time App rerenders, you create a brand new definition for ComponentList. Its code may have the same text as the previous one, but it's a different function, so a different component type as far as react is concerned. So react has to unmount the old ones and mount the new ones.

The fix is to move the component out. You'll need to change list to be a prop instead of a closure variable

function App() {
  const [list, setList] = useState([&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]);
  // ...
  &lt;ComponentList list={list}/&gt;
  // ...
}

function ComponentList({ list }){
  return(&lt;&gt;
    {list.map(i =&gt; &lt;Number num={i} key={i}/&gt;) }
  &lt;/&gt;)
}

答案2

得分: 1

你在父组件内创建了你的ComponentList,而且由于你没有使用useCallback,它在父组件重新渲染时被重新创建。
以下是如何实现这两种解决方案,但最佳解决方案是将你的组件移到父组件之外:

import React from 'react';
import './style.css';

// 解决方案 2
function ComponentList({ list }) {
  return (
    <>
      {list.map((i) => (
        <Number num={i} key={i} />
      ))}
    </>
  );
}

export default function App() {
  const [list, setList] = React.useState(['1', '2', '3', '4']);
  const [x, setX] = React.useState({});

  // 解决方案 1
  // const ComponentList = React.useCallback(() => {
  //     return (
  //         <>
  //             {list.map(i => <Number num={i} key={i}/>) }
  //         </>
  //     )
  // }, [])

  return (
    <div className="App">
      {/* <ComponentList /> 解决方案 1 */}
      <ComponentList list={list} /> {/* 解决方案 2 */}
      <div
        onClick={() => {
          setX({});
        }}
      >
        更新
      </div>
    </div>
  );
}

function Number({ num }) {
  const [color, setColor] = React.useState('rgb(0, 0, 0)');

  return (
    <div
      style={{ color: color }}
      onClick={() => {
        setColor('rgb(255, 255, 255)');
      }}
    >
      {num}
    </div>
  );
}

这里是在Stackblitz上的示例
此外,这是关于此问题的Jeff Herrington的视频

英文:

You created your ComponentList inside the parent, and since you don't use useCallback, it is recreated when the parent re-render.
Here is how to implement both solutions, but the best one is to take your component outside of the parent :

import React from &#39;react&#39;;
import &#39;./style.css&#39;;

// Solution 2
function ComponentList({ list }) {
  return (
    &lt;&gt;
      {list.map((i) =&gt; (
        &lt;Number num={i} key={i} /&gt;
      ))}
    &lt;/&gt;
  );
}

export default function App() {
  const [list, setList] = React.useState([&#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;]);
  const [x, setX] = React.useState({});

  // Solution 1
  // const ComponentList =  React.useCallback(() =&gt; {
  //     return(&lt;&gt;
  //         {list.map(i =&gt; &lt;Number num={i} key={i}/&gt;) }
  //     &lt;/&gt;)
  // }, [])

  return (
    &lt;div className=&quot;App&quot;&gt;
      {/* &lt;ComponentList /&gt; solution 1 */}
      &lt;ComponentList list={list} /&gt; {/* Solution 2 */}
      &lt;div
        onClick={() =&gt; {
          setX({});
        }}
      &gt;
        Update
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

function Number({ num }) {
  const [color, setColor] = React.useState(&#39;rgb(0, 0, 0)&#39;);

  return (
    &lt;div
      style={{ color: color }}
      onClick={() =&gt; {
        setColor(&#39;rgb(255, 255, 255)&#39;);
      }}
    &gt;
      {num}
    &lt;/div&gt;
  );
}

Here is the repro on Stackblitz
Also, here is a video of Jeff Herrington about this exact problem.

huangapple
  • 本文由 发表于 2023年4月20日 00:33:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76056885.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定