英文:
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 '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
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(["1", "2", "3", "4"]);
// ...
<ComponentList list={list}/>
// ...
}
function ComponentList({ list }){
return(<>
{list.map(i => <Number num={i} key={i}/>) }
</>)
}
答案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 'react';
import './style.css';
// Solution 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({});
// Solution 1
// const ComponentList = React.useCallback(() => {
// return(<>
// {list.map(i => <Number num={i} key={i}/>) }
// </>)
// }, [])
return (
<div className="App">
{/* <ComponentList /> solution 1 */}
<ComponentList list={list} /> {/* Solution 2 */}
<div
onClick={() => {
setX({});
}}
>
Update
</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>
);
}
Here is the repro on Stackblitz
Also, here is a video of Jeff Herrington about this exact problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论