代码之家  ›  专栏  ›  技术社区  ›  Wesley

在嵌套的重复组件中利用useState

  •  0
  • Wesley  · 技术社区  · 3 周前

    我有一个组件,如下所示,它本身工作得很好。其想法是,我正在建立一个比较运算符列表,其中第一个下拉列表中选择的项目是键值对集合中的一个键,其中该值是一个字符串数组,用于填充第三个框中与该键相关的选项。第二个框用作比较器选择器。

    这作为一个组件或嵌套组件非常有效。但一旦我在 map 它打破了钩子系统。

    很难将状态移出组件,因为每个迭代都需要自己的存储(也就是说,将其移动到父级,我们需要开始在索引上存储值,但只有父级知道索引,现在我们来回传递数据,这会使情况变得更糟)。

    这里的正确范例是什么?

    const Condition = () => {
        const [selectables, setSelectables] = useState([]);
        //This will be passed in as a prop
        const vals = {
            "Fruit" : ["Apple", "Orange"],
            "Vegetable" : ["Cucumber", "Celery", "Onion"],
        };
    
        const filterSelectablesOnClick = (selected) => {
            var newState = selected.map((item) => {
                return { label: item, value: item };
            });
            setSelectables(newState);
        };
    
        const valKeys = Object.entries(vals).map(([key, value]) => {
            return { label: key, value: key };
        })
    
        return (
            <Row>
                <div className="col-md-4">
                    <ReactSelect
                        options={valKeys}
                        onChange={(selected) => {
                            filterSelectablesOnClick(value);
                        }}
                    />
                </div>
                <div className="col-md-4">
                    <Form.Select aria-label="Default select example" className="form-select flex-grow-1">
                        <option>Choose one...</option>
                        <option value="is">is</option>
                        <option value="is-not">is-not</option>
                    </Form.Select>
                </div>
                <div className="col-md-4">
                    <ReactSelect
                        options={selectables}
                        isMulti
                        placeholder="Select comestible..."
                    />
                </div>
            </Row>
        );
    }
    

    这适用于以下三种情况中的两种:

    //In a single instance, fine
    <Condition />
    
    //In any number of multiple instances, lovely
    <Condition />
    <Condition />
    
    //But not as a dynamically created and iterated component:
    const [conditions, setConditions] = useState([]);
    
    const increaseConditionCount = () => {
      setConditions([...conditions, Condition()]);
    };
    
    {conditions.map((component, index) => (
      <React.Fragment key={index}>
      { component }
      </React.Fragment>
    ))}
    
    2 回复  |  直到 3 周前
        1
  •  1
  •   Nicholas Tower    3 周前
    setConditions([...conditions, Condition()]);
    

    您的主要问题是将组件作为函数调用( Condition() ),不将其呈现为元素( <Condition /> ). 这样做实际上并不是在呈现一个单独的组件,而是将额外的代码移植到了这个组件上。在这个过程中调用的所有钩子都会被计算为当前组件。因此,被调用的钩子的数量发生了变化,破坏了 rules of hooks .

    此外,虽然这不会导致您的问题,但我强烈建议您不要将元素存储在状态中。这样做很容易出现渲染过时数据的错误。相反,您的状态应该是描述要渲染的内容的最小数据量,然后在渲染过程中创建元素。

    根据您的描述,数字似乎是您唯一需要的状态:

    const [conditionsCount, setConditionsCount] = useState(0)
    
    const increaseConditionsCount = () => {
      setConditionsCount(prev => prev + 1)
    }
    
    {Array.from({ length: conditionsCount }).map((_, index) => (
      <Condition key={index}/>
    )}
    

    在许多情况下,您需要一个更复杂的状态来描述每个条件的属性,因此,如果您的情况需要,请随时将状态更改为包含一些数据的数组,然后将这些数据传递给 <条件/> 要素

        2
  •  0
  •   Essadrati    3 周前

    为什么要将组件存储在一个状态中?您可以将数据存储在状态中,并在需要时作为道具传递。 所以你可以做这样的事情

    const [conditionCount, setConditionCount] = useState(0);
    
    const increaseConditionCount = () => {
      setConditionCount(prevCount => prevCount + 1);
    };
    
    return (
      <>
        {[...Array(conditionCount)].map((_, index) => (
          <Condition key={index} />
        ))}
      </>
    );