ReactJs 管理一组有状态组件

ReactJs managing an array of stateful components

我有一个父组件,我想用它来动态添加和删除有状态组件,但是发生了一些奇怪的事情。

这是我的父组件以及我如何控制 FilterBlock

的列表
import React from 'react'
import FilterBlock from './components/FilterBlock'

export default function List() {
    const [filterBlockList, setFilterList] = useState([FilterBlock])
    
    const addFilterBlock = () => {
        setFilterList([...filterBlockList, FilterBlock])
    }

    const onDeleteFilterBlock = (index) => {
        const filteredList = filterBlockList.filter((block, i) => i !== index);
        setFilterList(filteredList)
    }

    return (
        <div>
            {
                filterBlockList.map((FilterBlock, index) => (
                    <FilterBlock
                        key={index}
                        onDeleteFilter={() => onDeleteFilterBlock(index)}
                    />
                ))
            }
            <button onClick={addFilterBlock}></button>
        </div>
    )
}

您可以假设 FilterBlock 是一个有状态的 React Hooks 组件。

我的问题是,每当我从任何添加的组件内部触发 onDeleteFilter 时,只有最后推送的 FilterBlock 会从列表中删除,即使我根据它在列表。

请在此处查看我的示例:

https://jsfiddle.net/emad2710/wzh5Lqj9/10/

问题是您将组件函数 FilterBlock 存储到状态并改变子组件内的父状态。

您可能知道直接改变状态会导致意想不到的错误。你在这里改变状态:

/* Child component */

function FilterBlock(props) {
    const [value, setValue] = React.useState('')
    const options = [
        { value: 'chocolate', label: 'Chocolate' },
        { value: 'strawberry', label: 'Strawberry' },
        { value: 'vanilla', label: 'Vanilla' }
    ]
    const onInputChange = (e) => {
        // child state has been changed, but parent state
        // does not know about it
        // thus, parent state has been mutated
        setValue(e.target.value)
    }
    
    return (
        <div>
            <button onClick={props.onDeleteFilter}>remove</button>
            <input value={value} onChange={onInputChange}></input>
              
        </div>
    )
}

/* Parent component */

const addFilterBlock = () => {
  setFilterList([...filterBlockList, FilterBlock])
}

filterBlockList.map((FilterBlock, index) => (
  <FilterBlock
    key={index}
    onDeleteFilter={() => onDeleteFilterBlock(index)}
  />
))

当 FilterBlock 值发生变化时,FilterBlock 会将其状态存储在 List 组件无法控制的地方。这意味着 filterBlockList 状态已发生变异。这会导致无法预测的错误,如上述问题。

为了克服这个问题,你需要在父组件中管理整个状态:

const {useCallback, useState} = React;


function FilterBlock(props) {

    const options = [
        { value: 'chocolate', label: 'Chocolate' },
        { value: 'strawberry', label: 'Strawberry' },
        { value: 'vanilla', label: 'Vanilla' }
    ]

    return (
        <div>
            <button onClick={props.onDeleteFilter}>remove</button>
            <input value={props.value} onChange={props.onChange}></input>
              
        </div>
    )
}


function App() {
    
    const [values, setValues] = useState(['strawberry']);
    
    const addFilterBlock = useCallback(() => {
            setValues(values.concat(''));
    }, [values]);

    const onDeleteFilterBlock = useCallback((index) => {
        setValues(values.filter((v, i) => i !== index));
    }, [values]);
    
    const setFilterValue = useCallback((index, value) => {
        const newValues = [...values];
        newValues[index] = value;
        setValues(newValues);
    }, [values]);

    return (
        <div>
            {
                values.map((value, index) => (
                    <FilterBlock
                        key={index}
                        onDeleteFilter={() => onDeleteFilterBlock(index)}
                        value={value}
                        onChange={(e) => setFilterValue(index, e.target.value)}
                    />
                ))
            }
            <button onClick={addFilterBlock}>add</button>
        </div>
    )
}


ReactDOM.render(<App />, document.querySelector("#app"))

JSFiddle