我创建了一个react-redux应用程序。目前它所做的是从服务器(api)加载课程,并将它们显示到课程组件。这非常有效。我试图添加一个功能,您可以通过将其发布到服务器来创建课程,然后服务器将成为一个成功对象。但是,当我发布到服务器时,我得到以下错误(见下文)。我认为这是由于我的connect语句监听加载课程操作。显然它认为应该得到一些东西,而不是成功对象。我已经尝试了一些事情来听它们的课程和成功的反应,但是为了节省你阅读我所做的所有奇怪事情的时间,我无法让它发挥作用。任何人都知道如何解决这个问题?
错误
TypeError: this.props.courses.map is not a function
course.component.js
onSave(){
// this.props.createCourse(this.state.course);
this.props.actions.createCourse(this.state.course);
}
render() {
return (
<div>
<div className="row">
<h2>Couses</h2>
{this.props.courses.map(this.courseRow)}
<input
type="text"
onChange={this.onTitleChange}
value={this.state.course.title} />
<input
type="submit"
onClick={this.onSave}
value="Save" />
</div>
</div>
);
}
}
// Error occurs here
function mapStateToProps(state, ownProps) {
return {
courses: state.courses
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(courseActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Course);
course.actions.js
export function loadCourse(response) {
return {
type: REQUEST_POSTS,
response
};
}
export function fetchCourses() {
return dispatch => {
return fetch('http://localhost:3000/api/test')
.then(data => data.json())
.then(data => {
dispatch(loadCourse(data));
}).catch(error => {
throw (error);
});
};
}
export function createCourse(response) {
return dispatch => {
return fetch('http://localhost:3000/api/json', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
response: response
})
})
.then(data => data.json())
.then(data => {
dispatch(loadCourse(data));
}).catch(error => {
throw (error);
});
};
}
course.reducer.js
export default function courseReducer(state = [], action) {
switch (action.type) {
case 'REQUEST_POSTS':
return action.response;
default:
return state;
}
}
server.js
router.get('/test', function(req, res, next) {
res.json(courses);
});
router.post('/json', function(req, res, next) {
console.log(req.body);
res.json({response: 200});
});
我已经尝试添加对状态的响应,并在地图状态中监听道具,但仍然出于某种原因反应是试图将响应映射到课程。我需要第二种连接方法吗?
function mapStateToProps(state, ownProps) {
return {
courses: state.courses,
resposne: state.resposne
};
}
从图片中可以看出,响应被映射为课程而不是响应。
答案 0 :(得分:3)
假设:
state.courses
最初是一个空数组 - 来自course.reducer.js fetchCourses()
操作fetchCourses()
也没有问题,只要server.js中的courses
是一个数组(响应中的数组替换初始{{1} }}) <强>流量:强>
现在我假设第一个渲染成功,React显示state.courses
和<input type="text">
按钮。现在当您输入标题并单击submit
按钮时,submit
方法会触发onSave()
操作,其参数与createCourse()
或多或少相似。
然后你序列化上面提到的param并发送到服务器(在course.actions.js - &gt; { title: 'something' }
),然后返回一个看起来像createCourse()
的响应(在server.js中) )。响应字段是一个整数,不是数组!继续使用对象{response: 200}
调用loadCourses()
,该对象会在课程中触发{response: 200}
。reducer.js
courseReducer
用整数替换 courseReducer()
(假设为[]]。并且此状态更新会触发重新呈现,并最终在整数上调用state.courses
,而不是在数组上调用,从而生成map()
。
可能的解决方案:
TypeError: this.props.courses.map is not a function
对象)或course
数组中,例如state.courses
根据OP的评论,如果您要做的是将新课程对象发送到服务器,验证它并发送成功(或错误),并根据响应将相同的课程对象添加到上一个课程列表,然后您可以简单地使用与return [...state, action.response]
对象loadData()
调用course
对象并使用(如上所述)在reducer中调用createCourse()
,而不是替换或改变旧数组创建一个新数组并追加课程对象,在es6中,您可以执行类似return [...state, course]
。
我建议你通过Redux's Doc。引自Redux Actions' Doc
操作是信息的有效负载,用于将数据从您的应用程序发送到您的商店。它们是商店唯一的信息来源。
使用有效负载调用createCourse()
操作,或多或少,
{title: 'Thing you entered in Text Field'}
,然后使用AJAX请求调用服务器并将 payload 传递给服务器,然后服务器验证有效负载并根据您的逻辑发送成功(或错误)响应。服务器响应看起来像{response: 200}
。这是createCourse()
操作的结束。现在,来自dispatch()
的{{1}} loadCourses()
操作,以及您从服务器收到的响应,这不是您想要的(根据您的评论)。所以,请尝试createCorse()
这样的操作(尝试重命名dispatch()
param,这有点令人困惑)
response
现在,//.....
.then(data => {
dispatch(loadCourse(response)); // the same payload you called createCourse with
})
//.....
是一个非常基本的操作,它只是转发参数,Redux用它来调用loadCourse()
。现在,如果您按照上一次讨论并更新了如何致电reducer
,那么loadCourse()
的回复就像
loadCourse()
然后传递到您的{
type: REQUEST_POSTS,
response: {
title: 'Thing you entered in Text Field',
}
}
,特别是您的reducer
。
操作描述了事情发生的事实,但未指定应用程序的状态更改的响应方式。这是减速器的工作。
courseReducer()
必须定义操作应如何影响reducer
内的数据的逻辑。
在store
中,您只需返回courseReducer()
对象中的response
字段,然后[期待] Redux即可自动神奇地改变您的状态!不幸的是,这不是发生的事情:(
无论你从减速器返回什么,完全取代之前的任何东西/对象,比如,如果你的状态看起来像这样
action
你从reducer那里返回这样的东西
{ courses: [{...}, {...}, {...}] }
然后redux会将状态更新为
{ title: 'Thing you entered in Text Field'}
state.courses不再是数组!
<强>解决方案:强>
将您的减速器改为这样的
{ courses: { title: 'Thing you entered in Text Field'} }
旁注:这可能会让人感到困惑,所以仅仅为了记录,export default function courseReducer(state = [], action) {
switch (action.type) {
case 'REQUEST_POSTS':
return [...state, action.response]
default:
return state
}
}
state
内的courseReducer()
不是完整的状态,而是{{1}在property
管理的state
上。您可以阅读有关此here
答案 1 :(得分:1)
- 在用不同的答案阅读你的评论后编辑,我已经删除了我以前的答案 -
您目前对行动和减少者采取的措施是,您在获取初始课程时正在调用loadCourse
。当您创建新课程时,也可以拨打loadCourse
。
在您的reducer中,您将直接返回API调用的响应。因此,当您获取所有课程时,您将获得所有课程的完整列表。但是如果你创建一个新的,你当前会收到一个说response: 200
的对象。 Object
没有地图功能,这可以解释您的错误。
如果您可以验证响应状态,我建议您在API上使用res.status(200).json()
并在前端切换响应状态(或使用then
和catch
validateStatus
3}}具有此功能(let initialState = {
courses: [],
createdCourse: {},
}
export default (state = initialState, action) => {
switch (action.type) {
case 'REQUEST_POSTS':
return {
...state,
courses: action.response
}
case 'CREATE_COURSE_SUCCESS':
return {
...state,
createdCourse: action.response,
}
default: return state;
}
}
))。
接下来,我将创建一个单独的动作类型来创建帖子,并在成功时分发。
我会将你的减速器更改为
if (manufactXiaomi.equalsIgnoreCase(android.os.Build.MANUFACTURER)) {
if (!session.getVisibilityOfAutoStartDialog()) {Intent intent = new Intent();
intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
startActivity(intent);}}
我不介意调查你的项目并给你一些关于如何改进一些事情的反馈(ES6,最佳实践,一般的东西)
答案 2 :(得分:1)
基于问题&amp;答案到目前为止,看起来你需要做这样的事情:
1)添加新操作并从createCourse函数
发送export function courseAdded(course, response) {
return {
type: 'COURSE_ADDED',
course
response
};
}
export function createCourse(course) {
return dispatch => {
return fetch('http://localhost:3000/api/json', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
course
})
})
.then(response => response.json())
.then(response => {
dispatch(courseAdded(course, response));
}).catch(error => {
throw (error);
});
};
}
2)更改缩减器以处理提取课程和添加新课程(我们在这里使用combineReducers来处理此问题)
import { combineReducers } from "redux";
function response(state = null, action) {
switch (action.type) {
case 'COURSE_ADDED':
return action.response;
default:
return state;
}
}
function courses(state = [], action) {
switch (action.type) {
case 'COURSE_ADDED':
return [...state, action.course];
case 'REQUEST_POSTS':
return action.response;
default:
return state;
}
}
export default combineReducers({
courses,
response
});
3)连接到response
组件
connect
州
function mapStateToProps(state, ownProps) {
return {
courses: state.courses,
response: state.response
};
}
4)如果你想展示它,请在你的组件中使用这个新的response
道具做一些事情。
// this is assuming response is a string
<span className="create-course-response">
Create Course Response - {this.props.response}
</span>
<强>更新强>
我已添加支持将新课程添加到现有课程列表的末尾,以及处理响应。你如何塑造状态完全取决于你,它可以相应地重新设置。
为了使此代码有效,您需要添加对spread operator的支持。如果您使用babel,可以像this那样完成。创建新对象对于确保不改变现有状态非常重要。这也意味着react-redux知道状态已发生变化。扩展运算符不是必需的,可以使用Object.assign
来完成,但该语法是丑陋的IMO。