简体   繁体   English

使用回调和挂钩(useState,useEffect)从父级中的 onClick 事件更改 React 子组件的 state

[英]Change React child component's state from an onClick event in parent, with callbacks and hooks (useState, useEffect)

My parent component contains an array of categories.我的父组件包含一个类别数组。 The component renders a list, and each list item has a checkbox, which is a child component.该组件渲染一个列表,每个列表项都有一个复选框,它是一个子组件。

I use useState() for the checkedCategories array, and for the checked/unchecked state of the the child component Checkbox.我将 useState() 用于 checkedCategories 数组,以及子组件 Checkbox 的选中/未选中 state。

  • If I check a checkbox, the category is added to the list.如果我选中一个复选框,该类别将添加到列表中。 The checkbox child component's state (checked) is updated in the child component (Checkbox.js)复选框子组件的 state(已选中)在子组件 (Checkbox.js) 中更新
  • If I uncheck a checkbox, the category is removed from the list.如果我取消选中复选框,该类别将从列表中删除。 The checkbox child component's state (checked) is updated in the child component (Checkbox.js).复选框子组件的 state(选中)在子组件 (Checkbox.js) 中更新。
  • I have a "clear all" button that updates the parent's checkedCategories state.我有一个“全部清除”按钮,用于更新父级的 checkedCategories state。
  • Every time the checkedCategories array is updated, I trigger a console.log with the useEffect hook, and this works for all three cases.每次更新 checkedCategories 数组时,我都会使用 useEffect 挂钩触发 console.log,这适用于所有三种情况。

There is one detail left: when the "clear all" button is clicked, all the checkboxes should be unchecked.剩下一个细节:单击“全部清除”按钮时,应取消选中所有复选框。 So I have to manipulate the checked state of all the Checkbox children somehow.所以我必须以某种方式操纵所有 Checkbox 子项的选中 state。

Parent component:父组件:

import {useState, useEffect} from "react";

import Checkbox from "../functions/Checkbox.js";

function CategoryList() {

    const categories = ['CategoryA','CategoryB', 'CategoryC']

    const [checkedCategories, setCheckedCategories] = useState([]);

    const addToCheckedCategories = id => {
        const updatedCheckedCategories = [...checkedCategories];
        updatedCheckedCategories.push(id);
        setCheckedCategories(updatedCheckedCategories);
    };

    const removeFromCheckedCategories = id => {
        const updatedCheckedCategories = checkedCategories.filter(cat => cat !== id);
        setCheckedCategories(updatedCheckedCategories);
    };

    const removeFilters = () => {
        //????
    }

    useEffect(() => {

        console.log('checked categories updated');
        console.log(checkedCategories);

        if (!checkedCategories.length) {
            console.log('the array is empty');

            //Set all the checkboxes' checked state to "false" somehow...?

        }

    }, [checkedCategories]);

    return(
        <div>
            <ul>
                {categories.map(categories =>
                     <li key={categories.toLowerCase()}>
                         <Checkbox id={categories.toLowerCase()}
                               label={categories}
                               addToCheckedCategories={addToCheckedCategories}
                               removeFromCheckedCategories={removeFromCheckedCategories}
                         />        
                    </li>
                 )}
            </ul>
           <button onClick={removeFilters}>Clear all</button>
      </div>
    )
}

export default CategoryList;

Child component:子组件:

import { useState } from 'react';

   function Checkbox({id, label, addToCheckedCategories, removeFromCheckedCategories}) {

   const [checked, setChecked] = useState(false);

   const handleChange = id => {

        if (checked) {
            removeFromCheckedCategories(id);
            console.log('removed ' + id);

        } else {
            addToCheckedCategories(id);
            console.log('added ' + id);

        }
        setChecked(!checked);
        console.log('changed value of checkbox');
    }

    return(
        <label htmlFor={id} >
            <input type="checkbox"
                   name="category-input"
                   id={id}
                   onChange={handleChange}
            />
            {label}
        </label>

    );
}

export default Checkbox;

I would lift the state completely to the parent making the Checkbox component stateless:我会将 state 完全提升到父级,使Checkbox组件无状态:

function Checkbox({
  id,
  label,
  checked,
  addToCheckedCategories,
  removeFromCheckedCategories,
}) {
  const toggle = () => {
    if (checked) {
      removeFromCheckedCategories(id);
    } else {
      addToCheckedCategories(id);
    }
  };

  return (
    <label htmlFor={id}>
      <input
        type="checkbox"
        name="category-input"
        id={id}
        onChange={() => toggle()}
        checked={checked}
      />
      {label}
    </label>
  );
}

From your parent you can pass down the checked property simply checking if that category is present in the checkedCategories array.您可以从您的父母那里传递checked属性,只需检查该类别是否存在于checkedCategories数组中。

function CategoryList() {
  const categories = ['CategoryA', 'CategoryB', 'CategoryC'];

  const [checkedCategories, setCheckedCategories] = useState([]);

  const addToCheckedCategories = (id) => {
    const updatedCheckedCategories = [...checkedCategories];
    updatedCheckedCategories.push(id);
    setCheckedCategories(updatedCheckedCategories);
  };

  const removeFromCheckedCategories = (id) => {
    const updatedCheckedCategories = checkedCategories.filter(
      (cat) => cat !== id
    );
    setCheckedCategories(updatedCheckedCategories);
  };

  // Remove filters is as easy as setting an empty array
  const removeFilters = () => {
    setCheckedCategories([]);
  };

  useEffect(() => {
    console.log('checked categories updated');
    console.log(checkedCategories);
  }, [checkedCategories]);

  return (
    <div>
      <ul>
        {categories.map((category) => (
          <li key={category.toLowerCase()}>
            <Checkbox
              id={category.toLowerCase()}
              label={category}
              checked={checkedCategories.includes(category.toLowerCase())}
              addToCheckedCategories={addToCheckedCategories}
              removeFromCheckedCategories={removeFromCheckedCategories}
            />
          </li>
        ))}
      </ul>
      <button onClick={removeFilters}>Clear all</button>
    </div>
  );
}

Using this approach clearing all is very easy, all you have to do is setting the checkedCategories array to an empty one.使用这种方法清除所有内容非常简单,您所要做的就是将checkedCategories数组设置为空。

Demo: https://stackblitz.com/edit/react-pwxgaq?file=src%2FApp.js演示: https://stackblitz.com/edit/react-pwxgaq?file=src%2FApp.js

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 React Hooks 从子组件 onClick() 触发父组件中的事件? - How to trigger an event in Parent Component from Child Component onClick() using React Hooks? React js从父组件更改子组件的state - React js change child component's state from parent component React Hooks useState+useEffect+event 给出过时的状态 - React Hooks useState+useEffect+event gives stale state 如何在React Native中从子组件更改父状态? - How to change parent's state from child component in react native? 如何在React Hooks中触发从子状态到父状态的改变? - How to trigger state change from child to parent in React Hooks? 如何从子组件反应钩子中仅更改 state 的一部分 - How to change only a part of the state from child component react hooks React hooks:在子函数内的 onClick 函数内更改父函数中的状态 - React hooks: change state in parent function inside onClick function inside child function 父 React 组件不会在使用反应钩子的子项中更改 state 时重新渲染; - Parent React component not re-rendering on change state in child using react hooks; 从子组件响应更改 UseState - React change UseState from Child Component 如何正确地将数组从父 React 类组件状态传递给使用钩子编写的子组件? - How to properly pass an array from a parent React class component state to a child component written with hooks?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM