首页 > 解决方案 > 如何从 React 中的父组件从道具读取数据到模态表单(受控)的状态?

问题描述

我有这个组件层次结构:

- Page 
-- Table 
-- ModalForm
-- Fab

我的组件层次结构

通过单击 Fab(右下角的圆形按钮),我打开空白 modalForm(material-ui 对话框组件)以创建新条目。我也想使用相同的表单来编辑表格中的条目。但是表单是受控的,有状态,打开时我无法从道具读取数据到状态(此事件不是 ComponentDidMounted 或其他类型的生命周期,只是改变了对话框组件的“打开”道具)。

这部分代码不起作用:

  componentWillReceiveProps() {
    const { fullname, birthday, gender } = this.props.data;
    console.log("this.props.data", this.props.data);
    this.setState({ fullname, birthday, gender });
    console.log("componentWillReceiveProps this.state ", this.state);
  }

这就是 Page(父组件)的外观:

import React, { Component } from "react";
import { Fab, CircularProgress } from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";

import Table from "./Table";
import Form from "./Form";

import { connect } from "react-redux";
import { firestoreConnect, isLoaded } from "react-redux-firebase";
import { compose } from "redux";

export class Page extends Component {
  state = { isModalOpen: false, data: {} };

  openModal = data => event => {
    const defaultData = data || { fullname: "Aaa", birthday: "1984-11-11", gender: "Жен" };

    this.setState({ data: defaultData });
    this.setState({ isModalOpen: true });

    console.log("defaultData", defaultData);
  };

  closeModal = () => {
    this.setState({ isModalOpen: false });
  };

  render() {
    const { athlets } = this.props;
    /*     if (athlets) {
      console.log("athlets.length", athlets.length);
    } */
    return (
      <main>
        {isLoaded(athlets) ? (
          <Table
            athlets={athlets}
            toggleModal={this.toggleModal}
            firestoreDelete={this.props.firestore.delete}
          />
        ) : (
          <CircularProgress />
        )}
        <Fab style={fabStyle} onClick={this.openModal(null)} color="primary" aria-label="Add">
          <AddIcon />
        </Fab>
        <Form
          isModalOpen={this.state.isModalOpen}
          data={this.state.data}
          closeModal={this.closeModal}
          firestoreAdd={this.props.firestore.add}
        />
      </main>
    );
  }
}

const mapStateToProps = state => {
  return {
    athlets: state.firestore.ordered.athlets
  };
};

export default compose(
  connect(mapStateToProps),
  firestoreConnect([{ collection: "athlets" }])
)(Page);

const fabStyle = {
  margin: 0,
  top: "auto",
  right: 20,
  bottom: 20,
  left: "auto",
  position: "fixed"
};

这就是子组件表单的外观:

import React from "react";

import {
  Button,
  TextField,
  Typography,
  FormHelperText,
  FormControl,
  InputLabel,
  Select,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle
} from "@material-ui/core";
// import { Link, Redirect } from "react-router-dom";
import styles from "./styles";
import { connect } from "react-redux";

// import { signIn } from '../../store/actions/authActions';
const initialState = { fullname: "", birthday: "", gender: "" };
class Form extends React.Component {
  state = initialState;

  //state = { ...this.props.data };
  componentWillReceiveProps() {
    const { fullname, birthday, gender } = this.props.data;
    console.log("this.props.data", this.props.data);
    this.setState({ fullname, birthday, gender });
    console.log("componentWillReceiveProps this.state ", this.state);
  }

  handleChange = e => {
    // console.log("e.target.id & value", e.target, e.target.value);
    // const id = e.target.id || "gender";
    this.setState({
      [e.target.id]: e.target.value
    });
  };

  handleSubmit = () => {
    const { fullname, birthday, gender } = this.state;
    console.log("this.state", this.state);
    const createdBy = { userName: this.props.profile.username, userId: this.props.auth.uid };
    const firestoreAdd = this.props.firestoreAdd(
      { collection: "athlets" },
      { fullname, birthday, gender, createdBy }
    );
    firestoreAdd.catch(error => {
      console.log("firestoreAdd error", error);
    });

    this.handleCancel();
  };

  handleDelete = () => {
    const { id, name, image } = this.state;
    const confirmation = window.confirm("Are you sure? ");
    if (confirmation) this.props.deleteAuthor({ id, name, image }, "authors");
    this.props.toggleModal();
  };

  handleCancel = () => {
    this.props.closeModal();
  };

  render() {
    const { authError } = this.props;
    //const { fullname, birthday, gender } = this.state;
    const { id, fullname, birthday, gender } = this.state;
    //if (auth.uid) return <Redirect to="/" />;
    return (
      <div style={styles.flexContainer}>
        <Dialog
          open={this.props.isModalOpen}
          onClose={this.handleClose}
          aria-labelledby="form-dialog-title"
        >
          <DialogTitle id="form-dialog-title">
            <Typography color="primary">Добавление спортсмена</Typography>
          </DialogTitle>
          <DialogContent>
            {/* FULLNAME */}
            <TextField
              onChange={this.handleChange}
              id="fullname"
              label="ФИО"
              type="text"
              value={fullname}
              margin="normal"
              autoFocus
              fullWidth
              InputLabelProps={{
                shrink: true
              }}
            />
            <br />
            {/* BIRTHDAY */}
            <TextField
              onChange={this.handleChange}
              id="birthday"
              label="Дата рождения"
              type="date"
              value={birthday}
              margin="normal"
              fullWidth
              InputLabelProps={{
                shrink: true
              }}
            />
            {/* GENDER */}
            <FormControl fullWidth /* className={classes.formControl} */>
              <InputLabel htmlFor="gender">Пол</InputLabel>
              <Select
                native
                value={gender}
                onChange={this.handleChange}
                inputProps={{
                  name: "gender",
                  id: "gender"
                }}
              >
                <option value="" />
                <option value="Муж">Муж</option>
                <option value="Жен">Жен</option>
              </Select>
            </FormControl>
            <br />
            <FormHelperText error>{authError ? authError : null}</FormHelperText>
            {/*           <FormHelperText>
            Don't have an accout? Please <Link to="/register">register</Link>.
          </FormHelperText> */}
          </DialogContent>
          <DialogActions>
            <Button /* onClick={this.handleDelete} */ color="secondary">Delete</Button>
            <Button onClick={this.handleCancel} color="primary">
              Cancel
            </Button>
            <Button onClick={this.handleSubmit} color="primary">
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    // authError: state.auth.authError,
    auth: state.firebase.auth,
    profile: state.firebase.profile
  };
};

const mapDispatchToProps = dispatch => {
  return {
    // signIn: creds => dispatch(signIn(creds))
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Form);

请分享想法,如果你有任何想法。

标签: reactjsmaterial-ui

解决方案


推荐阅读