React:在页面,NavBar和模态组件之间传递状态

I'm still trying to get the hang of passing state between components in React. I have a NavigationBar and RunSimsModal component, with a Ratings page.

The user tweaks inputs on the Ratings page which then contains inputs that go into RunSimsModal. I'm not sure how to do this as RunSimsModal is only accessible from NavigationBar. The NavigationBar does not know the Ratings state at any time. How do I go about passing data/state from Ratings to RunSimsModal ?

NavigationBar.js

import React, {useState} from "react";
import { Nav, Navbar, Form, FormControl, Button } from "react-bootstrap";
import styled from "styled-components";
import RunSimsModal from "./RunSimsModal";

const Styles = styled.div`
  .navbar {
    background-color: #222;
  }
  a,
  .navbar-nav,
  .navbar-light .nav-link {
    color: #9fffcb;
    &:hover {
      color: white;
    }
  }
  .navbar-brand {
    font-size: 1.4em;
    color: #9fffcb;
    &:hover {
      color: white;
    }
  }
  .form-center {
    position: absolute !important;
    left: 55%;
    right: 25%;
  }
`;

export const NavigationBar = function () {
  const [modalShow, setModalShow] = useState(false);
  return <Styles>
    <Navbar expand="lg" fixed ="top">
      <Navbar.Brand href="/">Decade3</Navbar.Brand>
      <Navbar.Toggle aria-controls="basic-navbar-nav" />
      <Form className="form-center">
        <FormControl type="text" placeholder="Search" className="" />
      </Form>
      <Navbar.Collapse id="basic-navbar-nav">
        <Nav className="ml-auto">
          <Button variant="primary" onClick={() => setModalShow(true)} >Run Sims</Button>{' '}
          <Nav.Item>
            <Nav.Link href="/">Home</Nav.Link>
          </Nav.Item>
          <Nav.Item>
            <Nav.Link href="/about">About</Nav.Link>
          </Nav.Item>
        </Nav>
      </Navbar.Collapse>
    </Navbar>

    <RunSimsModal show={modalShow} onHide={() => setModalShow(false)} />
  </Styles>
}

RunSimsModal.js

import React, { useState} from "react";
import { Button, Modal, FormFile, Form } from "react-bootstrap";
import * as outrightSimulatorApi from "../api/outrightSimulatorApi";
import Body from "../api/model/body.json";
import { withRouter } from "react-router-dom";

function RunSimsModal({ staticContext, ...props }) {
  const [numberofSims, setNumberOfSims] = useState("");
  const [simName, setSimName] = useState("");

  const runSims = event => {
    console.log("triggered");
    outrightSimulatorApi.runSim(
      Body.clientId,
      simName,
      numberofSims,
      Body.teamRatings, //This is the data I want to pass in from the Ratings page, it's an array
      Body.fixtureOverrides,
      Body.tournamentId
    );
    props.history.push("/reports");
  };

  return (
    <Modal
      {...props}
      size="lg"
      aria-labelledby="contained-modal-title-vcenter"
      centered
    >
      <Modal.Header closeButton>
        <Modal.Title id="contained-modal-title-vcenter">
          Run Simulations
        </Modal.Title>
      </Modal.Header>

      <Modal.Body>
        <h4>Run Sims</h4>

        <Form onSubmit={runSims}>
          <Form.Group controlId="formBasicEmail">
            <Form.Label>Number Of Simulations</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder="Enter number of sims"
              value={numberofSims}
              onChange={e => setNumberOfSims(e.target.value)}
            />
          </Form.Group>

          <Form.Group controlId="formBasicEmail">
            <Form.Label>Simulation Name</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder="Enter simulation name"
              value={simName}
              onChange={e => setSimName(e.target.value)}
            />
          </Form.Group>

          <Form.Group controlId="formBasicEmail">
            <Form.Label>Tournament Type</Form.Label>
            <Form.Control as="select">
              <option>LEAGUE</option>
            </Form.Control>
          </Form.Group>

          <Form.Group controlId="formBasicEmail">
            <Form.Label>Teams in tournament</Form.Label>
            <FormFile.Input />
          </Form.Group>

          <Button type="submit">Run</Button>
        </Form>
      </Modal.Body>
      <Modal.Footer>
        <Button onClick={props.onHide}>Close</Button>
      </Modal.Footer>
    </Modal>
  );
}

export default withRouter(RunSimsModal);

Ratings.js

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import { Table } from "react-bootstrap";
import NumericInput from "react-numeric-input";
import Teams from "../assets/Teams.js";
import { loadRatings } from "../actions/outrightSimulatorActions";
import ratingsStore from "../stores/ratingsStore";

const GridWrapper = styled.div`
  display: grid;
  grid-gap: 10px;
  margin-top: 4.5em;
  margin-left: 6em;
  margin-right: 6em;
  grid-template-columns: repeat(1, 1fr);
  grid-auto-rows: minmax(25px, auto);
`;

const TeamIcon = styled.span`
  margin-right: 0.5em;
`;

const teams = Teams;

function Display() {
  return (
    <GridWrapper>
      <div>
        <RatingsGrid />
      </div>
    </GridWrapper>
  );
}

function RatingsGrid() {
  const [ratings, setRatings] = useState(ratingsStore.getRatings());

  useEffect(() => {
    ratingsStore.addChangeListener(onChange);

    if (ratingsStore.getRatings().length === 0) loadRatings();

    return () => {
      ratingsStore.removeChangeListener(onChange);
    };
  }, []);

  function onChange() {
    console.log("ratings changed");
    setRatings(ratingsStore.getRatings());
  }

  return (
    <Table striped bordered hover size="sm">
      <thead>
        <tr>
          <th>Teams</th>
          <th>Ratings</th>
          <th>Home Advantage</th>
        </tr>
      </thead>
      <tbody>
        {ratings.map(rating => (
          <tr key={rating.teamId}>
            <td>
              <TeamIcon>
                <span>
                  <img
                    src={teams.find(t => t.id === rating.teamId).icon}
                    className="icon"
                    height="42"
                    width="42"
                    alt={rating.teamName}
                  />
                </span>
                {rating.teamName}
              </TeamIcon>
            </td>
            <td>
              <NumericInput
                value={rating.rating}
                step={0.01}
                mobile={false}
                size="14"
              />
            </td>
            <td>
              <NumericInput value={0.4} step={0.01} mobile={false} size="14" />
            </td>
          </tr>
        ))}
      </tbody>
    </Table>
  );
}

export default Display;
评论