繁体   English   中英

如何一次(同时)更新父 state 和子组件道具

[英]How to update parent state and child components props at once (at the same time)

我是 React 的新手,基本上我正在尝试一次更新父App.js组件的 state 及其子组件( Team.jsPlayer.js )道具。 目前只有父组件的 state 正在更新。 我将尝试一步一步地更好地解释它。

这里我有一个父组件App.js

export default function App() {
      const teams = [
        {
          Name: "Chicago Bulls",
          Players: ["Michael Jordan", "Dennis Rodman", "Scottie Pippen"],
          Championships: 6
        },
        {
          Name: "Golden State Warriors",
          Players: ["Stephen Curry", "Klay Thompson", "Draymond Green"],
          Championships: 5
        },
        {
          Name: "Los Angeles Lakers",
          Players: ["Kobe Bryant", "LeBron James", "Magic Johnson"],
          Championships: 17
        }
      ];
      const [selectedTeam, setSelectedTeam] = useState({});
    
      const players = [
        { Name: "LeBron James", MVPs: 4 },
        { Name: "Michael Jordan", MVPs: 5 },
        { Name: "Stephen Curry", MVPs: "2" }
      ];
      const [selectedPlayer, setSelectedPlayer] = useState({});
    
      const [modalContent, setModalContent] = useState(false);
      const clickedComponent = useRef(null);
      const [show, setShowModal] = useState(false);
    
      const showModal = () => {
        setShowModal(true);
      };
    
      const hideModal = () => {
        setShowModal(false);
      };
    
      const handleModalContent = (clicked) => {
        switch (clicked) {
          case "Team":
            clickedComponent.current = (
              <Team
                teams={teams}
                selectedTeam={selectedTeam}
                setSelectedTeam={setSelectedTeam}
              />
            );
            break;
          case "Player":
            clickedComponent.current = (
              <Player
                players={players}
                selectedPlayer={selectedPlayer}
                setSelectedPlayer={setSelectedPlayer}
              />
            );
            break;
          default:
            clickedComponent.current = null;
            break;
        }
      };
    
      return (
        <div className="App" style={{ justifyContent: "space-evenly" }}>
          <div
            style={{
              justifyContent: "center",
              width: "100%",
              display: "flex",
              flexWrap: "wrap",
              margin: "40px 0px 0px 0px"
            }}
          >
            <div
              className="table-cell"
              onClick={() => {
                handleModalContent("Team");
                setModalContent(true);
                showModal();
              }}
            >
              <div className="table-cell-text">Click to access Team component</div>
            </div>
            <div
              className="table-cell"
              onClick={() => {
                handleModalContent("Player");
                setModalContent(true);
                showModal();
              }}
            >
              <div className="table-cell-text">
                Click to access Player component
              </div>
            </div>
          </div>
          <h3 style={{ marginTop: "30px" }}>
            The last selected team was: {selectedTeam.Name}
            <br />
            The last selected player was: {selectedPlayer.Name}
          </h3>
          <Modal show={show} modalClosed={hideModal}>
            {(modalContent && clickedComponent.current) || null}
          </Modal>
        </div>
      );
    }

该组件有两个 arrays 对象( teamsplayers ),分别作为道具发送到TeamPlayer组件。 Team还接收selectedTeamsetSelectedTeam作为道具。 Player接收selectedPlayersetSelectedPlayer 这两个组件都有一个Modal组件和一个 select 输入。 Team组件中,用户将 select 一个团队,他们将显示所选球队的球员,而在Player组件中,一个球员将为 select 并显示所选球员的 MVP 数量。

Team.js

const Team = (props) => {
  return (
    <div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
      <h3>Select a team</h3>
      <div className="input-group col">
        <select
          onChange={(e) => {
            if (e === "") props.setSelectedTeam({});
            else {
              let foundTeam = props.teams.find(
                (team) => team.Name === e.target.value
              );
              props.setSelectedTeam(foundTeam);
            }
          }}
        >
          <option value="">Select a team...</option>
          {props.teams.map((team) => (
            <option key={team.Name} value={team.Name}>
              {team.Name}
            </option>
          ))}
        </select>
      </div>
      {Object.keys(props.selectedTeam).length > 0 ? (
        <div>
          <h3>{props.selectedTeam.Name} players: </h3>
          <br />
          {props.selectedTeam.Players.map((player, index) => (
            <div key={index}>{player}</div>
          ))}
        </div>
      ) : null}
    </div>
  );
};

export default Team;

Player.js

const Player = (props) => {
  return (
    <div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
      <h3>Select a player</h3>
      <div className="input-group col">
        <select
          onChange={(e) => {
            if (e === "") props.setSelectedPlayer({});
            else {
              let foundPlayer = props.players.find(
                (player) => player.Name === e.target.value
              );
              props.setSelectedPlayer(foundPlayer);
            }
          }}
        >
          <option value="">Select a player...</option>
          {props.players.map((player) => (
            <option key={player.Name} value={player.Name}>
              {player.Name}
            </option>
          ))}
        </select>
      </div>
      {Object.keys(props.selectedPlayer).length > 0 ? (
        <div>
          <h3>
            {props.selectedPlayer.Name} MVPs: {props.selectedPlayer.MVPs}
          </h3>
        </div>
      ) : null}
    </div>
  );
};

export default Player;

所以我的问题是,如果我 select 是子组件中的一个选项,他们不会立即收到更新的选定选项(我的意思是Team组件的selectedTeamPlayer组件的selectedPlayer ),但在父组件App中我更新了它们。 所以,如果我想让它们得到更新,我需要 select 一个选项,关闭模式并再次重新打开它们。

例如,这里我有App.js视觉对象:

在此处输入图像描述

如果我打开Team.js和 select 一个团队,我selectedTeam了在App.js中更新的团队,但在Team.js中没有:

在此处输入图像描述

因此,如果我关闭模式并再次重新打开Team组件,那么我更新了props.selectedTeam 所以我有以下内容:

在此处输入图像描述

我对Player组件有同样的问题,但在这种情况下,关于props.selectedPlayer

我怎样才能让它正常工作,我的意思是,我怎样才能在App中同时更新props.selectedTeamprops.selectedPlayer ,例如分别在TeamPlayer中? 谢谢!

代码沙盒

https://codesandbox.io/s/young-sun-gs117?file=/src/Team.js:51-1127

我知道的方法是只使用 Hooks useStateuseEffect并在 select 更改上更新 state 。

希望下面的Player示例有所帮助(在您的代码沙箱中工作,除非我没有回答您的问题):

import React, { useState, useEffect } from "react";
import "./styles.css";

const Player = (props) => {
  const [test, setTest] = useState("");

  useEffect(() => {
    console.log("props:", props);
    setTest(props.selectedPlayer);
  }, [props]);

  return (
    <div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
      <h3>Select a player</h3>
      <div className="input-group col">
        <select
          value={props.selectedPlayer}
          onChange={(e) => {
            if (e === "") props.setSelectedPlayer({});
            else {
              let foundPlayer = props.players.find(
                (player) => player.Name === e.target.value
              );
              props.setSelectedPlayer(foundPlayer);
              setTest(foundPlayer);
            }
          }}
        >
          <option value="">Select a player...</option>
          {props.players.map((player) => (
            <option key={player.Name} value={player.Name}>
              {player.Name}
            </option>
          ))}
        </select>
      </div>

      <h3>{test.Name} MVPs: {test.MVPs}</h3>
      {/* {Object.keys(props.selectedPlayer).length > 0 ? (
        <div>
          <h3>
            {props.selectedPlayer.Name} MVPs: {props.selectedPlayer.MVPs}
          </h3>
        </div>
      ) : null} */}
    </div>
  );
};

export default Player;

这是你需要做的,我重构了你的代码并添加了一些注释,这样你就知道我做了什么。 要记住的一件事是,您几乎不想将组件存储在 state 中。

export default function App() {
  const teams = [
    {
      Name: "Chicago Bulls",
      Players: ["Michael Jordan", "Dennis Rodman", "Scottie Pippen"],
      Championships: 6,
    },
    {
      Name: "Golden State Warriors",
      Players: ["Stephen Curry", "Klay Thompson", "Draymond Green"],
      Championships: 5,
    },
    {
      Name: "Los Angeles Lakers",
      Players: ["Kobe Bryant", "LeBron James", "Magic Johnson"],
      Championships: 17,
    },
  ];
  const players = [
    { Name: "LeBron James", MVPs: 4 },
    { Name: "Michael Jordan", MVPs: 5 },
    { Name: "Stephen Curry", MVPs: "2" },
  ];

  // This makes typo mistake less and will give you auto complete option
  const componentType = {
    team: "Team",
    player: "Player",
  };

  const [selectedTeam, setSelectedTeam] = useState({});
  const [selectedPlayer, setSelectedPlayer] = useState({});
  // the modalContent state and show state are doing the same thing so one of them is unneccessary
  const [show, setShowModal] = useState(false);
  const [clickedComponent, setClickedComponent] = useState("");

  const showModal = () => {
    setShowModal(true);
  };

  const hideModal = () => {
    setShowModal(false);
  };

  const handleModalContent = (clicked) => {
    setClickedComponent(clicked);
  };

  return (
    <div className="App" style={{ justifyContent: "space-evenly" }}>
      <div
        style={{
          justifyContent: "center",
          width: "100%",
          display: "flex",
          flexWrap: "wrap",
          margin: "40px 0px 0px 0px",
        }}
      >
        <div
          className="table-cell"
          onClick={() => {
            handleModalContent(componentType.team);
            showModal();
          }}
        >
          <div className="table-cell-text">Click to access Team component</div>
        </div>
        <div
          className="table-cell"
          onClick={() => {
            handleModalContent(componentType.player);
            showModal();
          }}
        >
          <div className="table-cell-text">
            Click to access Player component
          </div>
        </div>
      </div>
      <h3 style={{ marginTop: "30px" }}>
        The last selected team was: {selectedTeam.Name}
        <br />
        The last selected player was: {selectedPlayer.Name}
      </h3>
      <Modal show={show} modalClosed={hideModal}>
        {clickedComponent === componentType.player ? (
          <Player
            players={players}
            selectedPlayer={selectedPlayer}
            setSelectedPlayer={setSelectedPlayer}
          />
        ) : clickedComponent === componentType.team ? (
          <Team
            teams={teams}
            selectedTeam={selectedTeam}
            setSelectedTeam={setSelectedTeam}
          />
        ) : null}
      </Modal>
    </div>
  );
}

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM