React:如何从父组件在子组件中设置状态?

React: How to setState in child component from parent component?

我是 React 的新手,我正在尝试设置一个 Bootstrap 模式来显示警报消息。

在我的父 App.js 文件中,我有一个错误处理程序,它向 Modal.js 组件发送一个触发模态显示的道具,例如:

在 App.js:

function App() {

  const [modalShow, setModalShow] = useState(false);

  // Some other handlers

  const alertModalHandler = (modalMessage) => {
    console.log(modalMessage);
    setModalShow(true);
  }

  return (
   // Other components.
  <AlertModal modalOpen={modalShow}/>
  )
}

在 Modal.js 上:

import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";

const AlertModal = (props) => {

  const [isOpen, setIsOpen] = useState(false);

  if (props.modalOpen) {
    setIsOpen(true);
  }

  return (
    <Modal show={isOpen}>
      <Modal.Header closeButton>Hi</Modal.Header>
      <Modal.Body>asdfasdf</Modal.Body>
    </Modal>
  );
};

export default AlertModal;

但是,这不起作用。我收到错误:

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

如果我把Modal组件改成'dumb'组件,直接使用prop,例如:

 const AlertModal = (props) => {

  return (
    <Modal show={props.modalOpen}>
     <Modal.Header closeButton>Hi</Modal.Header>
     <Modal.Body>asdfasdf</Modal.Body>
     </Modal>
      );
    };

它确实有效,但我也想在 Modal.js 组件级别更改 show/hide 状态,例如,有一些东西可以处理那里的模态关闭按钮。

我不明白为什么会这样?

这是否意味着我必须在父 App.js 级别处理模态关闭功能?

编辑 - 完整 app.js 内容

import React, { useState } from 'react';

import './App.css';
import 'bootstrap/dist/css/bootstrap.css';
import AddUserForm from './components/addUserForm';
import UserList from './components/userList';
import AlertModal from './components/modal';

function App() {

  const [users, setUsers] = useState([]);
  const [modalShow, setModalShow] = useState(false);


  const addPersonHandler = (nameValue, ageValue) => {
    console.log(nameValue, ageValue);
  
    setUsers(prevUsers => {
      const updatedUsers = [...prevUsers];
      updatedUsers.unshift({ name: nameValue, age: ageValue });
      return updatedUsers;
    });
  };

  const alertModalHandler = (modalMessage) => {
    console.log(modalMessage);
    setModalShow(true);
  }

  let content = (
    <p style={{ textAlign: 'center' }}>No users found. Maybe add one?</p>
  );

  if (users.length > 0) {
    content = (
      <UserList items={users} />
    );
  }

  return (
    <>
      <div className="container">
        <div className="row">
          <div className="col-md-6 offset-md-3">
            <AddUserForm onAddPerson={addPersonHandler} fireAlertModal={alertModalHandler}/>
          </div>
        </div>
        <div className="row">
          <div className="col-md-6 offset-md-3">
            {content}
          </div>
        </div>
      </div>
      <AlertModal modalOpen={modalShow}/>
    </>
  );
}

export default App;

如果您已经将名为 modalShow 的状态发送到 AlertModal 组件,则没有理由使用另一个具有相同功能的状态作为 isOpen.

每当 modalShow 改变时,它会导致 AlertModal 组件的 re-render 因为你改变了它的状态,然后在里面如果 prop 为真,则设置另一个状态,当您设置 isOpen 时,会导致另一个不需要 re-render。然后,在每个 re-render 上,如果 props.showModal 没有改变(并且仍然是真的),你会一次又一次地触发 setIsOpen

如果你想控制 AlertModal 中的模态 open/close 我会这样做:

<AlertModal modalOpen={modalShow} setModalOpen={setModalShow}/>

showModal状态的set函数传递给modal组件,然后随意使用。例如,在 onClick 处理程序中。

modal.js:

import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";

const AlertModal = (props) => {

  const onClickHandler = () => {
       props.setModalOpen(prevState => !prevState)
  }

  return (
    <Modal show={props.modalOpen}>
      <Modal.Header closeButton>Hi</Modal.Header>
      <Modal.Body>asdfasdf</Modal.Body>
    </Modal>
  );
};

export default AlertModal;

在你的modal.js

你应该把

if (props.modalOpen) {
    setIsOpen(true);
  }

在 useEffect 中。

React.useEffect(() => {if (props.modalOpen) {
    setIsOpen(true);
  }}, [props.modalOpen])

你永远不应该那样调用 setState。如果你这样做,每个渲染都会 运行 并触发另一个渲染,因为你改变了状态。您应该将 setModalShow 与 if 子句一起放在 useEffect 中。例如:

useState(() => {
  if (modalOpen) {
      setIsOpen(true);
    }
}, [modalOpen])

请注意,我还从 props 中重构了 modalOpen。这样 useEffect 将仅在 modalOpen 更改时 运行。