React呈现未定义和更新状态

时间:2018-03-19 06:31:52

标签: reactjs react-router react-redux redux-thunk

执行CRUD操作时,我的代码完全正常。但是,当我尝试编辑或删除项目时,我遇到了一个小问题,该项目位于ProjectEdit.js文件中。我可以编辑和删除项目并将更新的数据存储到MongoDB,但是当我在editProject和deleteProject操作中使用history.push将ProjectEdit / ProjectDetail页面重定向到Project页面时,项目列表仍然显示我拥有的项目已编辑加上未定义的关键项目,我不知道它是如何存在的。但是,如果我刷新页面,就不会有任何问题。我认为问题可能是Redux商店,它包含我的应用程序的状态树,但我不确定。因此,我想知道你们是否可以帮助我。

Here's an undefined project key.

我的项目回购在这里:https://github.com/topkoong/PersonalWebsite

我的暂存网站如下:https://shielded-springs-57013.herokuapp.com/

感谢您的时间和考虑。

ProjectEdit.js

    import _ from 'lodash';
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { reduxForm, Field } from 'redux-form';
    import { Link } from 'react-router-dom';
    import ProjectField from './ProjectField';
    import formFields from './formFields';
    import * as actions from '../../actions';
    import { withRouter } from "react-router-dom";


    class ProjectEdit extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      componentDidMount() {
        const { _id } = this.props.match.params;
        this.props.fetchProject(_id);
      }

      componentWillReceiveProps({ project }) {
        if (project) {
          const { title, technology, description, creator } = project;
          this.setState({ title, technology, description, creator });
        }
      }

      onHandleSubmit = (event) => {
        event.preventDefault();
        event.stopPropagation();
        const { history} = this.props;
        this.props.editProject(this.props.match.params._id, this.state, history);
        //this.props.history.push("/project");
      }

      render() {
        return(
          <div>
            <form onSubmit={this.onHandleSubmit}>
              <div className="input-field">
                <input
                  value={this.state.title}
                  onChange={e => this.setState({ title: e.target.value })}
                  placeholder="Title"
                />
              </div>
              <div className="input-field">
                <input
                  value={this.state.technology}
                  onChange={e => this.setState({ technology: e.target.value })}
                  placeholder="Technology"
                />
              </div>
              <div className="input-field">
                <input
                  value={this.state.description}
                  onChange={e => this.setState({ description: e.target.value })}
                  placeholder="Description"
                />
              </div>
              <div className="input-field">
                <input
                  value={this.state.creator}
                  onChange={e => this.setState({ creator: e.target.value })}
                  placeholder="Creator"
                />
              </div>
              <Link to="/project" className="red btn-flat white-text">
                Cancel
              </Link>
              <button type="submit" className="teal btn-flat right white-text">
                Submit
                <i className="material-icons right">done</i>
              </button>
            </form>
          </div>
        );
      }


    }

    // ownProps is the prop obj that is going to ProjectDetail component up top.
    const  mapStateToProps = ({ projects, auth }, ownProps) => {
      return { auth, project: projects[ownProps.match.params._id]};
    }

    export default connect(mapStateToProps, actions)(withRouter(ProjectEdit));

ProjectDetail.js - 使用此组件显示和删除项目

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchProject, deleteProject } from '../../actions';
import { Link } from 'react-router-dom';
import { withRouter } from "react-router-dom";

class ProjectDetail extends Component {
    constructor(props) {
        super(props);
        this.state = {};
      }

    componentDidMount() {
        const { _id } = this.props.match.params;
        this.props.fetchProject(_id);
    }

    onDeleteClick() {
        console.log("Delete Project was clicked");
        const { history } = this.props;
        this.props.deleteProject(this.props.project._id, history);
    }

    render() {
        const { project } = this.props;
        if (!project) {
            return <div>Loading...</div>;
        }
        const { title, technology, description, creator, datePosted } = project;
        return (
            <div>
                <div className="row">
                    <div className="col s12 m9">
                        <h3>{title}</h3>
                        <h5>Technologies used: {technology}</h5>
                        <div className="card story">
                            <div className="card-content">
                                <span className="card-title">{new Date(datePosted).toLocaleDateString()}</span>
                                {description}
                            </div>
                        </div>
                    </div>
                    <div className="col s12 m3">
                        <div className="card center-align">
                            <div className="card-content">
                                <span className="card-title">{this.props.auth.displayName}</span>
                                <img className="circle responsive-img" src={this.props.auth.image}/>
                            </div>
                        </div>
                    </div>
                </div>
                <div className="row col s12">
                    <div className="col s6 offset-s1">
                        <Link className="waves-effect waves-light btn-large" to="/project">Back to project</Link>
                    </div>
                    <button className="btn-large red" onClick={() => this.onDeleteClick()}>Delete</button>
                </div>
            </div>
        );
    }
}
// ownProps is the prop obj that is going to ProjectDetail component up top.
function mapStateToProps({ projects, auth }, ownProps){
    return { auth, project: projects[ownProps.match.params._id] };
}

export default connect(mapStateToProps, { fetchProject, deleteProject })(withRouter(ProjectDetail));

Project.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProjectList from './project/ProjectList';
import { Link } from 'react-router-dom';

class Project extends Component {
  render() {
    return(
      <div>
        <h2>Project</h2>
        <ProjectList />
        {this.props.auth ? <div className="fixed-action-btn">
          <Link to="/project/new" className="btn-floating btn-large red">
            <i className="material-icons">add</i>
          </Link>
        </div> : ''}
      </div>
    );
  }
}

function mapStateToProps({ auth }) {
  return { auth };
}

export default connect(mapStateToProps)(Project);

ProjectList.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchProjects } from '../../actions';
import { Link } from 'react-router-dom';
import _ from 'lodash';


class ProjectList extends Component {

  componentDidMount() {
    this.props.fetchProjects();
  }
  renderProjects() {
    // return this.props.projects.reverse().map(project => {
    return _.map(this.props.projects, project => {
      return (
        <div className="row" key={project._id}>
          <div className="col s12 m6">
            <div className="card">
              <div className="card-image">
                {this.props.auth ? <Link to={`/project/${project._id}/edit`} className="btn-floating halfway-fab waves-effect waves-light red">
                <i className="material-icons">edit</i></Link> : ''}
              </div>
              <div className="card-content">
                <span className="card-title">{project.title}</span>
                <p>
                  <b>Technologies used: {project.technology} </b>
                </p>
                <p>
                  <b>Creator: {project.creator}</b>
                </p>
                <p>
                  <b>Project description: </b>{project.description}
                </p>
                <p className="right">
                  <b>Posted on: </b>{new Date(project.datePosted).toLocaleDateString()}
                </p>
              </div>
              <div className="card-action">
                <Link to={`/project/${project._id}`}>Read more</Link>
              </div>

            </div>
          </div>
        </div>
      );
    })
  }

  render() {
    return (
      <div>
        {this.renderProjects()}
      </div>
    );
  }
}

function mapStateToProps({ projects, auth }) {
  return { projects, auth };
}
export default connect(mapStateToProps, { fetchProjects })(ProjectList);

操作

export const editProject = (id, values, history) => async dispatch => {
  const res = await axios.put(`/api/projects/${id}/edit`, values);
  dispatch({ type: FETCH_PROJECTS, payload: res.data });
  history.push('/project');
}

export const deleteProject = (id, history) => async dispatch => {
  const res = await axios.delete(`/api/projects/${id}`);
  dispatch({ type: FETCH_PROJECTS, payload: res.data });
  history.push('/project');
}

减速

import mapKeys from 'lodash/mapKeys';
import { FETCH_PROJECT, FETCH_PROJECTS } from '../actions/types';

export default function(state = [], action) {
  switch (action.type) {
    case FETCH_PROJECTS:
      return { ...state, ...mapKeys(action.payload, '_id') };
    case FETCH_PROJECT:
      const project = action.payload;
      return { ...state, [project._id]: project };
    default:
      return state;
  }
}

API

const mongoose = require('mongoose');
const requireLogin = require('../middlewares/requireLogin');

const Project = mongoose.model('projects');

module.exports = app => {

  // show single project
  app.get('/api/projects/:id', async (req, res) => {
    const project = await Project.findOne({
      _id: req.params.id
    });
    res.send(project);
  });

  // edit single project
  app.put('/api/projects/:id/edit', requireLogin, async (req, res) => {
    try {
      const project = await Project.findOne({
        _id: req.params.id
      });
      project.title = req.body.title;
      project.technology = req.body.technology;
      project.description = req.body.description;
      project.creator = req.body.creator;
      project.datePosted = Date.now();
      project._user = req.user.id;
      await project.save();
      res.send(project);
    } catch (err) {
      res.status(422).send(err);
    }
  });

  // fetch projects

  app.get('/api/projects', async (req, res) => {
    const projects = await Project.find({ _user: req.user.id });
    // const projects = await Project.find({
    //   creator: "Theerut Foongkiatcharoen" 
    // });
    res.send(projects);
  });

  // create a new project

  app.post('/api/projects', requireLogin, async (req, res) => {
    const { title, technology, description, creator } = req.body;
    const project = new Project({
      title,
      technology,
      description,
      creator,
      datePosted: Date.now(),
      _user: req.user.id
    });
    try {
      await project.save();
      const user = await req.user.save();
      res.send(user);
    } catch (err) {
      res.status(422).send(err);
    }
  });

  // delete a single project

  app.delete('/api/projects/:id', requireLogin, async (req, res) => {
    try {
      const project = await Project.remove({
        _id: req.params.id
      });
      res.sendStatus(200);
    } catch (err) {
      res.status(422).send(err);
    }
  });
};

1 个答案:

答案 0 :(得分:0)

在代码中:

const res = await axios.delete(`/api/projects/${id}`);
dispatch({ type: FETCH_PROJECTS, payload: res.data }. 

此API端点是否仅返回HTTP状态代码,然后您将其发送到reducer?

这解释了您未定义的密钥。它未定义,因为您将其设置为响应中的非现有数据。