ReactJS + Redux编辑表单

时间:2018-11-27 08:23:26

标签: reactjs redux react-redux

我的格式如下:

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { updateExpert, fetchExpert } from "../../store/actions/expertActions";

class ExpertForm extends Component {
  state = {
    expert: {}
  };

  componentWillMount() {
    console.log("ComponentWillMount");
    const id = this.props.match.params.id;
    console.log("Will fetch expert with id", id);
    this.props.fetchExpert(id);
  }

  handleChange = e => {
    console.log(e);
    this.setState({
      expert: {
        ...this.state.expert,
        [e.target.id]: e.target.value
      }
    });
  };

  componentWillReceiveProps(nextProps) {
    const newExpert = nextProps.expert;
    console.log("got new expert ", newExpert);
    this.setState({
      expert: nextProps.expert
    });
  }

  handleSubmit = e => {
    e.preventDefault();
    const originalExpert = this.props.expert;
    console.log("Expert before", originalExpert);
    // const updatedExpert = {

    // firstName: this.state.expert.firstName,
    // lastName: this.state.expert.lastName,
    // bio: this.state.expert.bio,
    // country: originalExpert.country,
    // interestIds: originalExpert.interestIds,
    // city: originalExpert.city,
    // summary: originalExpert.summary,
    // websiteText: originalExpert.websiteText,
    // websiteUrl: originalExpert.websiteUrl
    // };
    const updatedExpert = this.state.expert;
    console.log("Expert after", updatedExpert);
    //call action
    this.props.updateExpert(originalExpert.userId, updatedExpert);
  };

  render() {
    const { expert } = this.props;
    return (
      <div className="container">
        <div className="card">
          <form onSubmit={this.handleSubmit} className="white">
            <div className="card-content">
              <h5 className="grey-text text-darken-3">Update expert</h5>
              <div className="row">
                <div className="input-field  col s6">
                  <label htmlFor="firstName">First Name</label>
                  <input
                    onChange={this.handleChange}
                    type="text"
                    id="firstName"
                  />
                </div>
                <div className="input-field col s6">
                  <label htmlFor="lastName">Last Name</label>
                  <input
                    onChange={this.handleChange}
                    type="text"
                    id="lastName"
                  />
                </div>
              </div>
              <div className="input-field">
                <label htmlFor="bio">Bio</label>
                <textarea
                  className="materialize-textarea"
                  id="bio"
                  onChange={this.handleChange}
                />
              </div>
              <div className="input-field">
                <label htmlFor="summary">Summary</label>
                <textarea
                  className="materialize-textarea"
                  id="summary"
                  onChange={this.handleChange}
                />
              </div>
              <div className="row">
                <div className="input-field col s6">
                  <label htmlFor="country">Country</label>
                  <textarea
                    className="materialize-textarea"
                    id="country"
                    onChange={this.handleChange}
                  />
                </div>
                <div className="input-field col s6">
                  <label htmlFor="city">City</label>
                  <textarea
                    className="materialize-textarea"
                    id="city"
                    onChange={this.handleChange}
                  />
                </div>
              </div>
              <div className="row">
                <div className="input-field col s6">
                  <label htmlFor="websiteText">Website text</label>
                  <textarea
                    className="materialize-textarea"
                    id="websiteText"
                    onChange={this.handleChange}
                  />
                </div>
                <div className="input-field col s6">
                  <label htmlFor="websiteUrl">Website URL</label>
                  <textarea
                    className="materialize-textarea"
                    id="websiteUrl"
                    onChange={this.handleChange}
                  />
                </div>
              </div>
            </div>
            <div className="card-action">
              <div className="input-field">
                <button className="btn pink lighten-1 z-depth-0">Update</button>
              </div>
            </div>
          </form>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  expert: state.experts.item
});

const mapDispatchToProps = dispatch => {
  return {
    updateExpert: (id, expert) => dispatch(updateExpert(id, expert)),
    fetchExpert: id => dispatch(fetchExpert(id))
  };
};

export default connect(
  mapStateToProps, //mapstatetoprops
  mapDispatchToProps //mapdispatchtoprops
)(ExpertForm);

现在,此表单主要用于编辑Expert类型的项目,而不添加它。这意味着我应该用数据库中已经存储的信息进行预填充。

但是,当我尝试直接在类似这样的输入上设置值时:

<input
                value={expert.firstName}
                onChange={this.handleChange}
                type="text"
                id="firstName"
              />

我收到以下错误:

index.js:1452 Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

这是ExpertList组件,用户可以从中访问此ExpertForm:

import React, { Component } from "react";
import PropTypes from "prop-types";
import ExpertItem from "./expert-item";
import { connect } from "react-redux";
import { Link } from "react-router-dom";

import { fetchExperts } from "../../store/actions/expertActions";

class ExpertList extends Component {
  componentWillMount() {
    console.log("ComponentWillMount");
    this.props.fetchExperts();
  }

  componentWillReceiveProps(nextProps) {
    console.log("Rceived new props");
  }

  render() {
    const { experts } = this.props;

    const expertsDom = experts.map(expert => (
      <Link to={"/expert/edit/" + expert.userId}>
        <ExpertItem key={expert.userId} expert={expert} />
      </Link>
    ));
    return <div className="expert-list section">{expertsDom}</div>;
  }
}

const mapStateToProps = state => ({
  experts: state.experts.items
});

export default connect(
  mapStateToProps,
  { fetchExperts }
)(ExpertList);

这些是我的动作:

import {
  FETCH_EXPERTS,
  UPDATE_EXPERT,
  ADD_EXPERT,
  FETCH_EXPERT
} from "./types";

import axios from "../../network/axios";

export const createExpert = expert => {
  return (dispatch, getState) => {
    //make async call to database

    dispatch({ type: ADD_EXPERT, expert: expert });
    // type: ADD_EXPERT;
  };
};

export const fetchExpert = id => {
  return (dispatch, getState) => {
    console.log("fetching expert with id ", id);
    axios
      .get("/connections/experts")
      .then(response => {
        const selectedExpert = response.data.filter(e => {
          return e.userId === id;
        })[0];
        console.log("ExpertsData ", selectedExpert);
        // const newState = Object.assign({}, this.state, {
        //   experts: newExperts
        // });
        dispatch({
          type: FETCH_EXPERT,
          payload: selectedExpert
        });
      })
      .catch(error => {
        console.log(error);
      });
  };
};

//Thunk allows us to call dispatch directly so that we can make async requests
//We can consider dispatch a resolver/promise, calling dispatch is just sending
//the data back
export const fetchExperts = () => {
  return (dispatch, getState) => {
    console.log("fetching");
    console.log("getstate ", getState());
    const accessToken = getState().auth.authInfo.accessToken;
    console.log("authToken ", accessToken);
    axios
      .get("/connections/experts")
      .then(response => {
        const newExperts = response.data;
        console.log("ExpertsData ", newExperts);
        // const newState = Object.assign({}, this.state, {
        //   experts: newExperts
        // });
        dispatch({
          type: FETCH_EXPERTS,
          payload: newExperts
        });
      })
      .catch(error => {
        console.log(error);
      });
  };
};

export const updateExpert = (id, expertData) => {
  return dispatch => {
    console.log("updating expert", id, expertData);
    axios
      .put("/experts/" + id, expertData)

      .then(response => {
        const updatedExpert = response.data;
        dispatch({
          type: UPDATE_EXPERT,
          payload: updatedExpert
        });
      })
      .catch(error => {
        console.log(error);
      });
  };
};

这是我的减速器:

import {
  FETCH_EXPERTS,
  UPDATE_EXPERT,
  FETCH_EXPERT
} from "../../store/actions/types";

const initialState = {
  items: [],
  item: {}
};

const expertReducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_EXPERT:
      console.log("reducer fetch by id");
      return {
        ...state,
        item: action.payload
      };
    case FETCH_EXPERTS:
      console.log("reducer fetch");
      return {
        ...state,
        items: action.payload
      };
    case UPDATE_EXPERT:
      console.log("reducer update");
      return {
        ...state,
        item: action.payload
      };
    default:
      return state;
  }
};

export default expertReducer;

2 个答案:

答案 0 :(得分:0)

如果要使用默认输入值,则需要使用默认值部分中的here,而不是使用value属性字段。

答案 1 :(得分:0)

问题在于,在加载Redux的状态之前,您的valueundefined。您可以通过默认为其提供一个空字符串来解决此问题,如下所示:

<input
    value={typeof expert.firstName === 'undefined' ? '' :  expert.firstName}
    onChange={this.handleChange}
    type="text"
    id="firstName"
/>