如何一次(同时)更新父状态和子组件 props

时间:2021-01-11 21:52:37

标签: javascript reactjs state

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

这里我有一个父组件 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>
      );
    }

这个组件有两个对象数组(teamsplayers),它们分别作为 props 发送到 TeamPlayer 组件。 Team 还接收 selectedTeamsetSelectedTeam 作为道具。 Player 接收 selectedPlayersetSelectedPlayer。两个组件都有一个 Modal 组件和一个选择输入。在 Team 组件中,用户将选择一个球队,并显示所选球队的球员,而在 Player 组件中,将选择一个球员,并显示他们的数量所选球员的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;

所以我的问题是,如果我在子组件中选择一个选项,他们不会收到更新的选定选项(我的意思是 selectedTeam 组件的 Team 和 {selectedPlayer {1}} 组件)立即但在父组件 Player 中我更新了它们。因此,如果我希望它们得到更新,我需要选择一个选项,关闭模态并再次重新打开它们。

例如,这里我有 App 视觉效果:

enter image description here

如果我打开 App.js 并选择一个团队,我会在 Team.js 中更新 selectedTeam 但在 App.js 中没有:

enter image description here

因此,如果我关闭模态并再次重新打开 Team.js 组件,那么我已更新 Team。所以我有以下几点:

enter image description here

我对 props.selectedTeam 组件有同样的问题,但在本例中是关于 Player

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

代码沙盒

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

2 个答案:

答案 0 :(得分:0)

我知道的方法是只使用 Hooks useStateuseEffect 并在选择更改时更新该状态。

希望 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;

答案 1 :(得分:0)

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

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>
  );
}