Проще всего это сделать, используя redux-thunk
. Этот пакет является перевождь промежуточного слоя, поэтому в первую очередь, вы должны подключить его к Redux:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
Это позволяет направить async
действия наряду с регулярными sync
действиями. Давайте создадим один из них:
// actions.js
export function fetchTodos() {
// Instead of plain objects, we are returning function.
return function(dispatch) {
// Dispatching REQUEST action, which tells our app, that we are started requesting todos.
dispatch({
type: 'FETCH_TODOS_REQUEST'
});
return fetch('/api/todos')
// Here, we are getting json body(in our case it will contain `todos` or `error` prop, depending on request was failed or not) from server response
// And providing `response` and `body` variables to the next chain.
.then(response => response.json().then(body => ({ response, body })))
.then(({ response, body }) => {
if (!response.ok) {
// If request was failed, dispatching FAILURE action.
dispatch({
type: 'FETCH_TODOS_FAILURE',
error: body.error
});
} else {
// When everything is ok, dispatching SUCCESS action.
dispatch({
type: 'FETCH_TODOS_SUCCESS',
todos: body.todos
});
}
});
}
}
Я предпочитаю разделять реагирующие компоненты на презентационные и контейнерные компоненты. Этот подход был полностью описан в this article.
Далее мы должны создать компонент TodosContainer
, который предоставит данные представительному компоненту Todos
. Здесь мы используем react-redux
библиотеку:
// TodosContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchTodos } from '../actions';
class TodosContainer extends Component {
componentDidMount() {
// When container was mounted, we need to start fetching todos.
this.props.fetchTodos();
}
render() {
// In some simple cases, it is not necessary to create separate `Todos` component. You can put todos markup directly here.
return <Todos items={this.props.todos} />
}
}
// This function is used to convert redux global state to desired props.
function mapStateToProps(state) {
// `state` variable contains whole redux state.
return {
// I assume, you have `todos` state variable.
// Todos will be available in container component as `this.props.todos`
todos: state.todos
};
}
// This function is used to provide callbacks to container component.
function mapDispatchToProps(dispatch) {
return {
// This function will be available in component as `this.props.fetchTodos`
fetchTodos: function() {
dispatch(fetchTodos());
}
};
}
// We are using `connect` function to wrap our component with special component, which will provide to container all needed data.
export default connect(mapStateToProps, mapDispatchToProps)(TodosContainer);
Кроме того, вы должны создать todosReducer
, который будет обрабатывать FETCH_TODOS_SUCCESS
действие, а другие 2 действия, если вы хотите, сообщение дисплей Загрузчик/ошибка.
// reducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
items: [],
isFetching: false,
error: undefined
};
function todosReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'FETCH_TODOS_REQUEST':
// This time, you may want to display loader in the UI.
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_TODOS_SUCCESS':
// Adding derived todos to state
return Object.assign({}, state, {
isFetching: false,
todos: action.todos
});
case 'FETCH_TODOS_FAILURE':
// Providing error message to state, to be able display it in UI.
return Object.assign({}, state, {
isFetching: false,
error: action.error
});
default:
return state;
}
}
export default combineReducers({
todos: todosReducer
});
Для других операций, как CREATE
, UPDATE
, DELETE
нет ничего особенного, они осуществляют таким же образом.
Большое вам спасибо за помощь. все еще пытаясь понять концепцию. как и где вы называете действие от компонента? Также (body => ({response, body}))) . then (({response, body}) => {{response, body}) => { 'делаю? Еще раз спасибо –
@JoKo, да, я скоро обновлю ответ. – 1ven
@JoKo, обновленный ответ – 1ven