当前位置: 首页 > 知识库问答 >
问题:

无法从子状态正确初始化父组件中的状态

令狐唯
2023-03-14

我有两个React组件,即FormSimpleCheckboxSimpleCheckbox使用了一些重要的UI组件,但我相信它们与我的问题无关。

表单中,使用effect调用api.getCategoryNames(),该函数解析为一个类别数组,例如,[“信息”、“调查”、“交易”、“痛苦”]

我的目标是访问父组件(表单)中复选框的状态(选中或未选中)。我采取了这个问题中建议的方法。(见验证答案)

有趣的是,当我记录检查时,它给出了(api调用解析后):

{Pain: false}

我所期望的是:

{
  Information: false,
  Investigation: false,
  Transaction: false,
  Pain: false,
}

此外,当我单击复选框时,会正确检查状态更新。例如,假设我已经选中了Information调查框,check变为以下内容:

{
  Pain: false,
  Information: true,
  Investigation: true,
}

这里是组件:

const Form = () => {
  const [checks, setChecks] = useState({});
  const [categories, setCategories] = useState([]);

  const handleCheckChange = (isChecked, category) => {
    setChecks({ ...checks, [category]: isChecked });
  }

  useEffect(() => {
    api
      .getCategoryNames()
      .then((_categories) => {
        setCategories(_categories);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  return (
    {categories.map(category => {
       <SimpleCheckbox 
         label={category}
         onCheck={handleCheckChange}
         key={category}
         id={category}
       />
    }
  )
}

const SimpleCheckbox = ({ onCheck, label, id }) => {
  const [check, setCheck] = useState(false);

  const handleChange = (event) => {
    setCheck(event.target.checked);
  };

  useEffect(() => {
    onCheck(check, id);
  }, [check]);

  return (
    <FormControl>
      <FormControlLabel
        control={
          <Checkbox checked={check} onChange={handleChange} color="primary" />
        }
        label={label}
      />
    </FormControl>
  );
}

共有2个答案

锺离昂然
2023-03-14

我缺少的是在setChecks中使用功能更新。钩子API参考说:如果使用以前的状态计算新状态,您可以将函数传递给setState。

所以改变后:

const handleCheckChange = (isChecked, category) => {
    setChecks({ ...checks, [category]: isChecked });
  }

const handleCheckChange = (isChecked, category) => {
    setChecks(prevChecks => { ...prevChecks, [category]: isChecked });
  }

它已经开始像我预期的那样工作了。

何和惬
2023-03-14

看起来您需要两次控制状态,分别在表单级别和复选框组件级别。

我消除了其中一个状态并更改了处理程序。此外,我将检查设置为具有initialState,这样您就不会得到非受控到受控输入警告

import React, { useState, useEffect } from "react";
import { FormControl, FormControlLabel, Checkbox } from "@material-ui/core";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <Form />
    </div>
  );
}

const Form = () => {
  const [checks, setChecks] = useState({
    Information: false,
    Investigation: false,
    Transaction: false,
    Pain: false
  });
  const [categories, setCategories] = useState([]);
  console.log("checks", checks);
  console.log("categories", categories);
  const handleCheckChange = (isChecked, category) => {
    setChecks({ ...checks, [category]: isChecked });
  };

  useEffect(() => {
    // api
    //   .getCategoryNames()
    //   .then(_categories => {
    //     setCategories(_categories);
    //   })
    //   .catch(error => {
    //     console.log(error);
    //   });
    setCategories(["Information", "Investigation", "Transaction", "Pain"]);
   
  }, []);

  return (
    <>
      {categories.map(category => (
        <SimpleCheckbox
          label={category}
          onCheck={handleCheckChange}
          key={category}
          id={category}
          check={checks[category]}
        />
      ))}
    </>
  );
};

const SimpleCheckbox = ({ onCheck, label, check }) => {
  return (
    <FormControl>
      <FormControlLabel
        control={
          <Checkbox
            checked={check}
            onChange={() => onCheck(!check, label)}
            color="primary"
          />
        }
        label={label}
      />
    </FormControl>
  );
};

如果希望api动态提供检查,可以编写一个fetchHandler,等待api的结果并更新两个状态片

  const fetchChecks = async () => {
    let categoriesFromAPI = ["Information", "Investigation", "Transaction", "Pain"] // api result needs await
    setCategories(categoriesFromAPI);
    let initialChecks = categoriesFromAPI.reduce((acc, cur) => {
      acc[cur] = false
      return acc
    }, {})
    setChecks(initialChecks)
   
  }
  useEffect(() => {
    fetchChecks()
  }, []);

我硬编码了categoriesFromApi变量,请确保在api调用语句前面添加wait

let categoriesFromApi = await axios.get(url)

最后,将状态的初始切片设置为空对象

const [checks, setChecks] = useState({});
 类似资料:
  • 问题内容: 在React中,这两种实现之间有什么真正的区别?一些朋友告诉我,FirstComponent是模式,但是我不明白为什么。SecondComponent看起来更简单,因为渲染仅被调用一次。 第一: 第二: 更新:我将setState()更改为this.state = {}(感谢joews),但是,我仍然看不到区别。一个比另一个好吗? 问题答案: 应该注意的是,复制永远不会更改为状态的属性

  • 问题内容: 在React中,这两种实现之间有什么真正的区别?一些朋友告诉我,FirstComponent是模式,但是我不明白为什么。SecondComponent似乎更简单,因为渲染仅被调用一次。 第一: 第二: 更新:我将setState()更改为this.state = {}(感谢乔伊斯),但是,我仍然看不到区别。一个比另一个好吗? 问题答案: 应该注意的是,复制永远不会更改为状态的属性是一种

  • 我有两个组件:父组件,我想改变子组件的状态: 和子组件: 我需要从父组件更改子组件的打开状态,或者当单击父组件中的按钮时,从父组件调用子组件的toggleMenu()?

  • 假设我的父组件有两个子组件: 我从Child2获得一个输入,并将其传递给父组件(到目前为止,我知道该怎么做)。但是,我需要将该输入传递给Child1,以更新它的状态。 我怎么能那么做?

  • 我已经开始学习ReactJS,有一个关于无状态和有状态组件的问题。一般来说,我遵循组件和容器的分离,如下所示。有状态函数在组件文件夹中,其他逻辑操作在容器文件夹下。文件夹结构 让我们思考材料UI下拉列表。 为了打开和关闭下拉菜单和方法更改打开状态,这意味着它是有状态组件。但没有其他变化(省略年龄设置)。它似乎是可重用的组件,但包括状态与非常简单的操作,如打开和关闭。我应该放入哪个文件夹?容器还是组