簡體   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