如何更改多个React子组件的状态?

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

How to change state of multiple React children?

问题

以下是翻译好的内容:

<SidebarOption /><SideBarContainers /> 分别是具有名为 open 的状态的项目,当它们被点击时会突出显示,并且它们位于 <SidebarItemList /> 内。

    <SidebarOption id="1" name="Startsida: Fjärrvärmenät" />
    <SidebarOption id="2" name="Startsida: Användare" />
    <SideBarContainers id="3" name="Allmänt" 
    options={[
        "Senaste uppdaterad", "Visa Pluginmoduler", "Öppna Arkivet", "Personliga inställningar", 
        "Regionala inställningar", "Om Driftportalen"
        ]}/>
    <SideBarContainers id="4" name="Planering" notPressAble={[0, 6, 11]}
    options={[
        "Aktiviteter:", "+ Ny Aktivitet", "Visa Alla Öppna", "Visa Försenat", "Visa Alla Stängda", "Visa Alla Kategoriserat",
        "Att göra:", "+ Ny Att göra", "Visa Alla Öppna", "Visa Alla Stängda", "Visa Alla Kategoriserat", 
        "Personal planering:", "+ Ny post", "Visa Alla enkel"
    ]} />
    </SideBarItemList>

下面是 <SideBarItemList /> 类,我想要更改项目的 open 值,以便如果一个项目被突出显示,而另一个项目被按下,我希望所有其他项目的状态 open 都设置为 false,以便只有一个项目的状态 open 可以设置为 true。问题是项目的状态 open 似乎不可扩展,我该怎么办?

我的错误:无法添加属性 open,对象不可扩展


class SideBarItemList extends Component{
    constructor(props){
        super(props);
        this.state ={
            sidebarItems: null
        };
    }

    static getDerivedStateFromProps(props, state){
        return{
            sidebarItems: props.children
        };
    }

    handleItems = () => {
        let openNumber = null;
        for(let x = 0; x < this.state.sidebarItems.length; x++)
        {
            if(this.state.sidebarItems[x].open = true)
            {
                openNumber = x;
            }
            this.state.sidebarItems[x].open = false;
        }

        this.state.sidebarItems[openNumber].open = true;
    }

    render(){
        return(
            <div onClick={this.handleItems}>
                {this.state.sidebarItems}
            </div>
        )
    }
}

export default SideBarItemList;

以下是 <SideBarConatiners /> 的脚本,希望能够澄清:

import classes from "../../layout/Sidebar.module.css";
import { Link } from "react-router-dom";

class SidebarOption extends Component{
    constructor(props){
        super(props);
        this.state = {
            open: false,
            
        }
    }

    setOpen = () => {
        this.setState(prevState => {
            return {
                open: !prevState.open
            }
        })
    }

    setStates = () => {
        this.setOpen()
    }

    render(){
        return (
        <div>
            <Link to={this.props.link}>
                <button
                className={this.state.open ? classes.dropdownbtnopen: classes.dropdownbtn} 
                onClick={this.setStates}>{this.props.name}
                </button>
            </Link>
        </div>
        )
    }
}

export default SidebarOption;
英文:

Down below is <SidebarOption /> and <SideBarConatiners /> these are both items that have a state called open to highlight them whenever they have been clicked and they are inside the <SidebarItemList />

       <SideBarItemList>
        <SidebarOption id="1" name="Startsida: Fjärrvärmenät" />
        <SidebarOption id="2" name="Startsida: Användare" />
        <SideBarContainers id="3" name="Allmänt" 
        options={[
            "Senaste uppdaterad", "Visa Pluginmoduler", "Öppna Arkivet", "Personliga inställningar", 
            "Regionala inställningar", "Om Driftportalen"
            ]}/>
        <SideBarContainers id="4" name="Planering" notPressAble={[0, 6, 11]}
        options={[
            "Aktiviteter:", "+ Ny Aktivitet", "Visa Alla Öppna", "Visa Försenat", "Visa Alla Stängda", "Visa Alla Kategoriserat",
            "Att göra:", "+ Ny Att göra", "Visa Alla Öppna", "Visa Alla Stängda", "Visa Alla Kategoriserat", 
            "Personal planering:", "+ Ny post", "Visa Alla enkel"
        ]} />
        </SideBarItemList>

Down below is the <SideBarItemList /> class, i want to change the open value of the items soo that if one item is higlighted and another item is pressed i want all the other items to set the state open to false so that only one item state open can be set to true the problem is that the items state open does not seem to be extensible what can i do about this?

my error: Cannot add property open, object is not extensible

import { Component } from "react";

class SideBarItemList extends Component{
    constructor(props){
        super(props);
        this.state ={
            sidebarItems: null
        };
    }

    static getDerivedStateFromProps(props, state){
        return{
            sidebarItems: props.children
        };
    }

    handleItems = () => {
        let openNumber = null;
        for(let x = 0; x < this.state.sidebarItems.length; x++)
        {
            if(this.state.sidebarItems[x].open = true)
            {
                openNumber = x;
            }
            this.state.sidebarItems[x].open = false;
        }

        this.state.sidebarItems[openNumber].open = true;
    }

    render(){
        return(
            <div onClick={this.handleItems}>
                {this.state.sidebarItems}
            </div>
        )
    }
}

export default SideBarItemList;

Script below of <SideBarConatiners /> added to hopefully clarify

import React, {Component} from "react";
import classes from "../../layout/Sidebar.module.css"
import { Link } from "react-router-dom";

class SidebarOption extends Component{
    constructor(props){
        super(props);
        this.state = {
            open: false,
            
        }
    }

    setOpen = () => {
        this.setState(prevState => {
            return {
                open: !prevState.open
            }
        })
    }

    setStates = () => {
        this.setOpen()
    }



    render(){
        return (
        <div>
            <Link to={this.props.link}>
                <button
                className={this.state.open ? classes.dropdownbtnopen: classes.dropdownbtn} 
                onClick={this.setStates}>{this.props.name}
                </button>
            </Link>
        </div>
        )
    }
}

export default SidebarOption;

答案1

得分: 0

您可以使用 cloneElement(element, props) 来添加或覆盖子元素的属性值 文档

您需要更改 SideBarItemList 的实现以跟踪本地状态中选定的子元素,并在状态更改时重新渲染并向所选的子元素提供 isSelected: true,对其他所有子元素提供 isSelected: false(或不提供)。

另一种选择(我不建议这样做)是以这样的方式实现所有子元素,以提供一种命令式接口(通过 forwardRefuseImperativeHandle)允许像 child_37.collapse()child_03.expand() 这样的方法调用。

您的实现可以看起来像这样:

export const SideBarItemList = ({ children }) => {
  const [selected, setSelected] = useState();

  return (
    <ul>
    {
      React.Children.map(children, (child, index) => (
        <li onClick={() => setSelected(index)}>
          {React.cloneElement(child, { isOpen: index === selected })}
        </li>
      ))
    }
    </ul>
  );
};
英文:

You can add or override prop values for children with cloneElement(element, props) doc.

You would need to change your implementation of SideBarItemList to track in local state which child element is selected, and whenever the state changes you would re-render and provide isSelected: true to the one selected child element and isSelected: false (or nothing) to all others.

An alternative (I don't recommend here) is to implement all children in such a way as to offer an imperative interface (via forwardRef and useImperativeHandle) that would allow a method call like child_37.collapse() and child_03.expand().

Your implementation could look like this:

export const SideBarItemList = ({ children }) =&gt; {
  const [selected, setSelected] = useState();

  return (
    &lt;ul&gt;
    {
      React.Children.map(children, (child, index) =&gt; (
        &lt;li onClick={() =&gt; setSelected(index)}&gt;
          {React.cloneElement(child, { isOpen: index === selected })}
        &lt;/li&gt;
      ))
    }
    &lt;/ul&gt;
  );
};

huangapple
  • 本文由 发表于 2023年6月5日 15:46:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76404432.html
匿名

发表评论

匿名网友

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

确定