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

3

我刚接触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组件更改为“哑”组件并直接使用prop,例如:
 const AlertModal = (props) => {

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

它确实可以工作,但我想在Modal.js组件层面上改变显示/隐藏状态,例如有些东西在那里处理模态框关闭按钮。

我不明白为什么会出现问题?

这是否意味着我必须在父级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;

你能分享完整的App.js代码吗?如果其余的代码不可见,很难确定问题出在哪里。 - Konrad Przydział
@KonradPrzydział 我已经在上面添加了它。 - MeltingDog
你确定你在 <AddUserForm /> 内正确绑定了事件处理程序吗? - Konrad Przydział
@KonradPrzydział 是的 - console.log 有效。 - MeltingDog
3个回答

1
在你的modal.js中,你应该放置以下内容:
if (props.modalOpen) {
    setIsOpen(true);
  }

在useEffect中。

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

1

您不应该随意调用 setState。如果这样做,它将在每次渲染时运行并触发另一个渲染,因为您已更改了状态。您应该将 setModalShow 与 if 子句一起放在 useEffect 中。例如:

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

请注意,我还将modalOpenprops中重构出来。这样,useEffect只会在modalOpen发生变化时运行。

抱歉,我不确定我理解最后一部分。你是如何得到modalOpen的?它由props构成的结构是什么? - MeltingDog

0
如果您已经发送了一个名为modalShow的状态到AlertModal组件,那么没有理由使用另一个执行相同工作的状态,如isOpen
每当更改modalShow时,它会导致AlertModal组件重新渲染,因为您更改了它的状态,然后在属性为真时,再设置另一个状态,导致不必要的重新渲染。然后,在每次重新渲染时,如果props.showModal没有更改(仍为真),则会反复触发setIsOpen
如果您想在AlertModal中控制模态打开/关闭,我建议采取以下措施:
<AlertModal modalOpen={modalShow} setModalOpen={setModalShow}/>

showModal状态的set函数传递给模态组件,然后按照需要使用它。例如,在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;

是的,我也这么想。但这意味着我必须将关闭按钮函数放在app.js上 - 即完全将它们与modal.js组件分离。这似乎有点奇怪。这是正确的做法吗? - MeltingDog
在我看来,你需要为AddUserForm和AlertModal都设置setModalOpen,它们都是App的子组件,因此这个设置函数应该放在App.js中。如果你想要一个关闭按钮功能,它可以留在AlertModel内部,在这个关闭函数中,你可以使用props中接收到的setShowModal并将其设置为false。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接