Javascript ReactJS:在重新渲染期间,类型数组的状态正在分解为元素

Javascript ReactJS:在重新渲染期间,类型数组的状态正在分解为元素,javascript,reactjs,react-hooks,react-component,react-state,Javascript,Reactjs,React Hooks,React Component,React State,我想做的事 我有一个带有一系列“配置文件”的主组件。对于每个概要文件,我有两个不同的条件组件(一次只显示一个)。这两个组件中的每一个都有一个按钮,用于在单击时切换组件。因此,我将状态提升到主组件,使用“useState”钩子创建组件状态(这是一个数组,其中每个索引都是一个字符串,表示profiles数组中每个元素要显示的子组件),我为这些单击创建了两个事件处理函数,并将它们作为渲染道具传递到其子组件中 它们从第一个子组件开始 问题&我是如何发现的 const Peers = props =>

我想做的事

我有一个带有一系列“配置文件”的主组件。对于每个概要文件,我有两个不同的条件组件(一次只显示一个)。这两个组件中的每一个都有一个按钮,用于在单击时切换组件。因此,我将状态提升到主组件,使用“useState”钩子创建组件状态(这是一个数组,其中每个索引都是一个字符串,表示profiles数组中每个元素要显示的子组件),我为这些单击创建了两个事件处理函数,并将它们作为渲染道具传递到其子组件中

它们从第一个子组件开始

问题&我是如何发现的

const Peers = props => {
    let dummyPeer = {
        _id: "9asdf98sj3942j4fs9ji",
        user: {
            name: "Test Peer",
            avatar: "//www.gravatar.com/avatar/cd56136f6d9abfdf4a0198dc9ce656c8?s=200&r=pg&d=mm"
        },
        bio: "Biography for Test Peer",
        year: "2022",
        courses: [
            "CISC124",
            "PSYC223",
            "PSYC236",
            "COMM200",
            "CISC251"
        ]
    }

    let profiles = [];
    profiles.push(dummyPeer);

    let initialState = [];
    profiles.forEach(profile => {
        initialState.push("normal");
    });

    let [viewState, setViewState] = useState(initialState);
    console.log(viewState);

    const openLargeView = (id) => {
        let changeIndex = profiles.map(profile => profile._id).indexOf(id);
        setViewState(state => state[changeIndex] = "large");
        console.log(viewState);
    }

    const closeLargeView = (id) => {
        let changeIndex = profiles.map(profile => profile._id).indexOf(id);
        setViewState(state => state[changeIndex] = "normal");
        console.log(viewState);
    }

    return (
        <Fragment>
            {profiles.map((profile, index) => (<Fragment key={profile._id} >
                {viewState[index] === "normal" ? (
                    <Peer openLargeView={openLargeView} profile={profile} />
                ) : (
                    <ViewPeer closeLargeView={closeLargeView} profile={profile} />
                )}
            </Fragment>))}
        </Fragment>
    )
}
当您按下按钮切换到其他组件时,它会工作。当你按下按钮返回时,它崩溃了。正在说“TypeError:无法分配给字符串“large”的只读属性“0”。在useState初始化之后,以及在每个函数中的状态更改调用之后,我放置了一些console.log(state)。发生的事情(只有一个元素的测试列表)是

  • 组件初始化时,状态显示为[‘正常’](原始状态,良好)
  • 单击第一个组件的按钮时,['normal']变为['large'](按预期)
  • 现在重新渲染组件时,状态变为“大”(不再是数组)
  • 单击第二个组件的按钮时,应用程序崩溃,因为它不再是数组,所以无法更改数组元素
  • 主要部件

    const Peers = props => {
        let dummyPeer = {
            _id: "9asdf98sj3942j4fs9ji",
            user: {
                name: "Test Peer",
                avatar: "//www.gravatar.com/avatar/cd56136f6d9abfdf4a0198dc9ce656c8?s=200&r=pg&d=mm"
            },
            bio: "Biography for Test Peer",
            year: "2022",
            courses: [
                "CISC124",
                "PSYC223",
                "PSYC236",
                "COMM200",
                "CISC251"
            ]
        }
    
        let profiles = [];
        profiles.push(dummyPeer);
    
        let initialState = [];
        profiles.forEach(profile => {
            initialState.push("normal");
        });
    
        let [viewState, setViewState] = useState(initialState);
        console.log(viewState);
    
        const openLargeView = (id) => {
            let changeIndex = profiles.map(profile => profile._id).indexOf(id);
            setViewState(state => state[changeIndex] = "large");
            console.log(viewState);
        }
    
        const closeLargeView = (id) => {
            let changeIndex = profiles.map(profile => profile._id).indexOf(id);
            setViewState(state => state[changeIndex] = "normal");
            console.log(viewState);
        }
    
        return (
            <Fragment>
                {profiles.map((profile, index) => (<Fragment key={profile._id} >
                    {viewState[index] === "normal" ? (
                        <Peer openLargeView={openLargeView} profile={profile} />
                    ) : (
                        <ViewPeer closeLargeView={closeLargeView} profile={profile} />
                    )}
                </Fragment>))}
            </Fragment>
        )
    }
    
    const Peers=props=>{
    让dummyPeer={
    _id:“9asdf98sj3942j4fs9ji”,
    用户:{
    名称:“测试对等方”,
    阿凡达://www.gravatar.com/avatar/cd56136f6d9abfdf4a0198dc9ce656c8?s=200&r=pg&d=mm
    },
    传记:“测试同行传记”,
    年份:“2022年”,
    课程:[
    “CISC124”,
    “心理223”,
    “心理236”,
    “通信200”,
    “CISC251”
    ]
    }
    设profiles=[];
    profiles.push(dummyPeer);
    让initialState=[];
    profiles.forEach(profile=>{
    初始状态。推送(“正常”);
    });
    let[viewState,setViewState]=useState(initialState);
    console.log(viewState);
    const openLargeView=(id)=>{
    让changeIndex=profiles.map(profile=>profile.\u id).indexOf(id);
    setViewState(state=>state[changeIndex]=“大”);
    console.log(viewState);
    }
    const closeLargeView=(id)=>{
    让changeIndex=profiles.map(profile=>profile.\u id).indexOf(id);
    setViewState(状态=>state[changeIndex]=“正常”);
    console.log(viewState);
    }
    返回(
    {profiles.map((profile,index)=>(
    {viewState[index]=“正常”(
    ) : (
    )}
    ))}
    )
    }
    
    子组件1:

    const Peer = ({ profile, openLargeView }) => {
        const { _id, user, bio, year, courses } = profile;
        const { avatar } = user;
    
        return (<Fragment>
            <div className="card-row">
                <div className="profile-header">
                    <h1 className="peer-text row-title"> {user.name} </h1>
                    <p className="peer-text peer-small"> {year} </p>
                    <img className="avatar avatar-peer-small" src={avatar} alt='' />
                </div>
                <button onClick={() => openLargeView(_id)} className="btn-small"> More </button>
            </div>
         </Fragment>)
    }
    
    const Peer=({profile,openLargeView})=>{
    const{u id,user,bio,year,courses}=profile;
    const{avatar}=用户;
    返回(
    {user.name}
    

    {year}

    openLargeView(_id)}className=“btn small”>更多信息 ) }
    儿童部分2:

    const ViewPeer = ({ profile, closeLargeView }) => {
        const { _id, user, bio, year, courses } = profile;
        const { avatar } = user;
    
        let courseElements = courses.map((course, index) =>
        <li key={index} className="profile-text"> {course} </li>
        );
    
        return (
            <Fragment>
                <div className="card-md peer-card">
                    <div className="profile-header">
                        <h1 className="peer-text"> {user.name} </h1>
    
                        <img className="avatar avatar-peer" src={avatar} alt='' />
                    </div>
    
                    <div className="profile-info">
                        <h2 className="profile-text"> {bio} </h2>
                        <h2 className="profile-text2"> Year: {year} </h2>
                        <ul className="course-list"> {courseElements} </ul>
    
                        <div className="profile-button-group">
                            <button onClick={() => closeLargeView(_id)} className="btn-small"> Close </button>
                            <button className="btn-small"> Send Buddy Request </button>
                        </div>
                    </div>
                </div>
                </Fragment>
        )
    }
    
    const ViewPeer=({profile,closeLargeView})=>{
    const{u id,user,bio,year,courses}=profile;
    const{avatar}=用户;
    让courseElements=courses.map((课程,索引)=>
    
  • {course}
  • ); 返回( {user.name} {bio} 年份:{Year}
      {courseElements}
    closeLargeView(_id)}className=“btn small”>关闭 发送好友请求 ) }
    预期和实际结果


    我希望当单击第一个组件的按钮时,它会返回到原始组件,但状态变成字符串数组,应用程序崩溃。

    这里的问题是
    viewState
    openLargeView()
    closeLargeView()中更新的方式

    调用这些函数时,对
    setViewState
    的调用调用状态更改回调,该回调实际上将
    viewState
    的类型从数组更改为字符串:

    /* 
    Summary of problem with following line of code:
    1. The statement: state[changeIndex] = "large" returns the string "large"
    2. When executed, the statement returns the "large" string from the callback
    3. The viewState therefore becomes a string with value "large"
    */
    setViewState(state => state[changeIndex] = "large"); 
    
    考虑将这些状态更新修改为以下内容:

    setViewState(state => {
        /* 
        1. Shallow clone state into a new array with ... spread
        2. Assign value of "large" to the "changeIndex" in cloned array
        3. Return cloned array as new state for viewState
        */
        const arrayClone = [...state];
        arrayClone[changeIndex] = "large";
        return arrayClone;
    });
    
    这确保了
    setViewState()
    回调传回组件的状态为数组类型,这是组件所期望的。一个更完整的示例显示了是否需要进行所有更改:

    const Peers = props => {
    
        const profiles = [{
            _id: "9asdf98sj3942j4fs9ji",
            user: {
                name: "Test Peer",
                avatar: "//www.gravatar.com/avatar/" + 
                        "cd56136f6d9abfdf4a0198dc9ce656c8?s=200&r=pg&d=mm"
            },
            bio: "Biography for Test Peer",
            year: "2022",
            courses: [
                "CISC124",
                "PSYC223",
                "PSYC236",
                "COMM200",
                "CISC251"
            ]
        }]
    
        let [viewState, setViewState] = useState(["normal"]);
    
        const openLargeView = (id) => {
            let changeIndex = profiles.map(profile => profile._id).indexOf(id);
            setViewState(state => {
                const arrayClone = [...state];
                arrayClone[changeIndex] = "normal";
                return arrayClone;
            });
        }
    
        const closeLargeView = (id) => {
            let changeIndex = profiles.map(profile => profile._id).indexOf(id);
            setViewState(state => {
                const arrayClone = [...state];
                arrayClone[changeIndex] = "large";
                return arrayClone;
            });
        }
    
        return (
            <Fragment>
                {profiles.map((profile, index) => (<Fragment key={profile._id} >
                    {viewState[index] === "normal" ? (
                        <Peer openLargeView={openLargeView} profile={profile} />
                    ) : (
                        <ViewPeer closeLargeView={closeLargeView} profile={profile} />
                    )}
                </Fragment>))}
            </Fragment>
        )
    }
    
    const Peers=props=>{
    常量配置文件=[{
    _id:“9asdf98sj3942j4fs9ji”,
    用户:{
    名称:“测试对等方”,
    阿凡达://www.gravatar.com/avatar/“+
    “cd56136f6d9abfdf4a0198dc9ce656c8?s=200&r=pg&d=mm”
    },
    传记:“测试同行传记”,
    年份:“2022年”,
    课程:[
    “CISC124”,
    “心理223”,
    “心理236”,
    “通信200”,
    “CISC251”
    ]
    }]
    让[viewState,setViewState]=useState([“正常”]);
    const openLargeView=(id)=>{
    让changeIndex=profiles.map(profile=>profile.\u id).indexOf(id);
    setViewState(状态=>{
    常数阵列气旋=[…状态];
    ArraCyclone[changeIndex]=“正常”;
    回流式旋风分离器;
    });
    }
    const closeLargeView=(id)