React 呈现未定义和更新的状态

Posted

技术标签:

【中文标题】React 呈现未定义和更新的状态【英文标题】:React renders both undefined and updated state 【发布时间】:2018-08-27 15:53:20 【问题描述】:

我的代码在执行 CRUD 操作时运行良好。但是,当我尝试编辑或删除位于 ProjectEdit.js 文件中的项目时,我遇到了一个小问题。我可以编辑和删除项目并将更新的数据存储到 MongoDB,但是当我在 editProject 和 deleteProject 操作中使用 history.push 将我的 ProjectEdit/ProjectDetail 页面重定向到项目页面时,项目列表仍然显示我拥有的项目已经编辑加上一个未定义的关键项目,我不知道它是如何存在的。但是,如果我刷新页面,则完全没有问题。我认为问题可能是包含我的应用程序状态树的 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】:

在代码中:

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

这个 API 端点不是只返回一个 HTTP 状态代码,然后您将其发送到 reducer?

这解释了您未定义的键。它未定义,因为您将其设置为响应中不存在的数据。

【讨论】:

修改怎么样?

以上是关于React 呈现未定义和更新的状态的主要内容,如果未能解决你的问题,请参考以下文章

检查jQuery中的未定义和空值

无法读取未定义和未处理的承诺拒绝的属性“捕获”

具有未定义和空值的排序对象数组

REACT + REDUX:在 redux 状态更改时 mapStateToProps 更新状态,但视图未按照新状态呈现

ReactJS 状态未正确呈现

成员变量 - 未定义和不可访问