功能组件的子组件的属性为什么使用了过时的状态?

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

Why are a functional component's child component's props using outdated state?

问题

以下是您要翻译的内容:

Parent.js

import { useState, useEffect } from 'react';
import Rooms from './Rooms';

export default function TourOverview(props) {
   const [roomList, setRoomList] = useState([]);

   useEffect(() => {  // add one room as default that cant be removed
        setRoomList([<Rooms addRoomCount={addRoomCount} id={1} key={1} />]);
    }, []);

    const addRoomCount = () => {
        let tempArr = roomList; //浅拷贝
        tempArr.push(<Rooms removeRoomCount={removeRoomCount} id={roomList.length + 1} key={roomList.length + 1} />);
        setRoomList([...tempArr]);
    }

    const removeRoomCount = (id) => {
       // 不相关部分保留为空
    }

    return (
        <>
          {roomList}
        </>
    )

Child.js

import { useState, useEffect } from 'react';
export default function Rooms(props, ref) {

   return (
         <>
           房间 {props.id}
           {props.addRoomCount !== undefined ? <button type="button" className="btn btn-link"  onClick={props.addRoomCount}>
                + 添加房间
            </button> : 
                <button type="button" className="btn btn-link" onClick={() => props.removeRoomCount(props.id)}>
                    - 移除
                </button>
            }
    </>
)

请注意,翻译中将 HTML 实体(例如 &lt;&gt;)转换为相应的角括号符号(<>),并将 &quot; 转换为双引号(")。

英文:

I am actually using Next JS but its the same. i have simplified the code and removed the irrelevant part.

What i am building is a form where i can add new fields (child component) dynamically but the default will be 1 and there must always be minimum 1 field.

Parent.js

import {useState, useEffect} from &#39;react&#39;
import Rooms from &#39;./Rooms&#39;

export default function TourOverview(props) {
   const [roomList, setRoomList] = useState([])

   useEffect(()=&gt;{  // add one room as default that cant be removed
        setRoomList([&lt;Rooms addRoomCount={addRoomCount} id={1} key={1} /&gt;])
    }, [])

    const addRoomCount=()=&gt; {
        let tempArr = roomList //shallow copy
        tempArr.push(&lt;Rooms removeRoomCount={removeRoomCount} id={roomList.length+1} key={roomList.length+1} /&gt;)
        setRoomList([...tempArr])
    }

    const removeRoomCount=(id)=&gt;{
       // will leave empty as not relevant
    }

    return (
        &lt;&gt;
          {roomList}
        &lt;/&gt;
    )

Child.js

import {useState, useEffect} from &#39;react&#39;
export default function Rooms(props, ref) {

   return (
         &lt;&gt;
           Room {props.id}
           {props.addRoomCount != undefined ? &lt;button type=&quot;button&quot; className=&quot;btn btn-link&quot;  onClick={props.addRoomCount}&gt;
                + Add Room
            &lt;/button&gt; : 
                &lt;button type=&quot;button&quot; className=&quot;btn btn-link&quot; onClick={()=&gt;props.removeRoomCount(props.id)}&gt;
                    - Remove
                &lt;/button&gt;
            }
    &lt;/&gt;
)

From the code you can see that as the parent component loads for the first time it will automatically add a child component that has the "add room" button, the others added later will have only "remove" button.

This doesnt work, it looks ok initially, but the moment you click on "add room" button, the roomList state gets reset to the state when the one time useEffect was initially loaded, which is...empty.

In another words, the addRoomCount() assigned as a prop to the first <Rooms/>, is using the state roomList during the initial load when its still empty, its like a time machine.

I tried the class component version of this and it works. So its only functional component wont work.

Anyone can explain why?

答案1

得分: 2

不要在状态中存储JSX。相反,使用状态来创建要渲染的元素。

在这种情况下,只需将id数组存储为状态。

const [roomList, setRoomList] = useState([1]);
const addRoomCount = () => setRoomList([...roomList, roomList.length + 1]);
// ...
return roomList.map(i => <Room addRoomCount={addRoomCount} id={i} key={i}
			removeRoomCount={i ? removeRoomCount : undefined} />);
英文:

Don't store JSX in state. Instead, use the state to create the elements to render.

In this case, it suffices to store the array of ids as state.

const [roomList, setRoomList] = useState([1]);
const addRoomCount = () =&gt; setRoomList([...roomList, roomList.length + 1]);
// ...
return roomList.map(i =&gt; &lt;Room addRoomCount={addRoomCount} id={i} key={i}
			removeRoomCount={i ? removeRoomCount : undefined} /&gt;);

huangapple
  • 本文由 发表于 2023年7月17日 23:42:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706107.html
匿名

发表评论

匿名网友

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

确定