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 呈现未定义和更新的状态的主要内容,如果未能解决你的问题,请参考以下文章
REACT + REDUX:在 redux 状态更改时 mapStateToProps 更新状态,但视图未按照新状态呈现