redux数据请求机制
image.png
图片来源于:https://segmentfault.com/img/bVRQRK?w=1205&h=618
action & actionCreator
action creator 就是函数而已,负责构建一个 action (是的,action creator 这个名字已经很明显了)并返回它。通过几行简单的代码就可以解释清楚了!
const actionCreator = function () {
return {
type : 'AN_ACTION'
}
}
一般约定 action 是一个拥有 type 属性的对象。
console.log(actionCreator())
// { type: 'AN_ACTION' }
reducer
Reducer 函数只是一个纯函数,它接收应用程序的当前状态以及发生的 action,然后返回修改后的新状态(或者有人称之为归并后的状态)。Reducer 函数是 action 的订阅者。
const reducer = function (state = {}, action) {
console.log('reducer was called with state', state, 'and action', action);
return state;
}
Store
以上,action描述“发生了什么”,而reducer根据action来更新state。但是他们两者之间是如何关联的呢?
不用担心,Redux 会帮你把action和reducer连接起来。
我们把 Redux实例称为 store 并用以下方式创建:
import { createStore } from 'redux'
const store_0 = createStore(() => {})
注意:在createStore时,需要给它传入一个 reducer 函数。
每当一个action发生时,Redux都能调用这个函数。往 createStore 传 Reducer 的过程就是给 Redux绑定 action处理函数(也就是Reducer)的过程。
接下来,试着在 Reducer 中打印一些 log
const reducer = function (...args) {
console.log('Reducer was called with args', args)
}
const store_1 = createStore(reducer)
// 输出:Reducer was called with args [ undefined, { type: '@@redux/INIT' } ]
我们没有dispatch(分发)任何action,但是reducer被调用了!这是由于初始化应用state的时候,Redux dispatch 了一个初始化的 action ({ type: '@@redux/INIT' })。reducer的入参为(state, action)。state还没有被初始化,自然为undefined。
如何读取store中的state?
Redux为我们提供了store.getState()方法。
import { createStore } from 'redux'
const reducer_2 = function (state = {}, action) {
console.log('reducer_2 was called with state', state, 'and action', action)
return state;
}
const store_2 = createStore(reducer_2)
// 输出: reducer_2 was called with state {} and action { type: '@@redux/INIT' }
console.log('store_2 state after initialization:', store_2.getState())
// 输出: store_2 state after initialization: {}
如何dispatch action?
我们需要使用store.dispatch(action)方法。
// 接以上代码
const anAction = {
type : 'AN_ACTION'
}
store_2.dispatch(anAction);
// 输出:reducer_2 was called with state {} and action { type: 'AN_ACTION' }
combineReducers
combineReducer用于合并Reducers,并且合并对应的State。
const userReducer = function (state = {}, action) {
console.log('userReducer was called with state', state, 'and action', action)
switch (action.type) {
// etc.
default:
return state;
}
}
const itemsReducer = function (state = [], action) {
console.log('itemsReducer was called with state', state, 'and action', action)
switch (action.type) {
// etc.
default:
return state;
}
}
import { createStore, combineReducers } from 'redux'
const reducer = combineReducers({
user : userReducer,
items : itemsReducer
})
// 输出:
// userReducer was called with state {} and action { type: '@@redux/INIT' }
// userReducer was called with state {} and action { type: '@@redux/PROBE_UNKNOWN_ACTION_9.r.k.r.i.c.n.m.i' }
// itemsReducer was called with state [] and action { type: '@@redux/INIT' }
// itemsReducer was called with state [] and action { type: '@@redux/PROBE_UNKNOWN_ACTION_4.f.i.z.l.3.7.s.y.v.i' }
var store_0 = createStore(reducer)
// 输出:
// userReducer was called with state {} and action { type: '@@redux/INIT' }
// itemsReducer was called with state [] and action { type: '@@redux/INIT' }
console.log('store_0 state after initialization:', store_0.getState())
// 输出:
// store_0 state after initialization: { user: {}, items: [] }
回过头来看看文章开头的数据流向图
View组件通过click等事件,dispatch一个(actionCreator返回的)action,通过Store把当前状态state和action传递给订阅者reducer函数,reducer返回一个新的状态存储在Store中,Store又把新的State传递给View组件触发组件更新。
为了将Redux和React联系到一起。就需要用到React-Redux这个库。
import { connect } from 'react-redux'
const containerComponent = connect(mapStateToProps, mapDispatchToProps)(presentationalComponent)
简单来说,mapStateToProps和mapDispatchToProps就是分别把Redux的state,和dispatch(action)映射到React组件中作为props。connect将展示组件(presentationalComponent)封装成高阶的容器组件(containerComponent)。state的更新意味着props更新。
上诉摘自于https://segmentfault.com/a/1190000010407887
请求机制:
接下来上项目吧:
- 安装依赖:
cnpm install redux react-redux redux-thunk axios --save -
目录结构如下
image.png
3.代码如下:
(1)src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
(2) src/App.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { requestBlog } from './actions/blog';
class App extends Component {
//第一步:通过UI动作进入后,在componentDidMount请求数据
//通过requestBlog()找到Blog.js的actionCreater
componentDidMount() {
this.props.requestBlog();
}
render() {
//随着blog.js中的值被更改,this.props的值(依赖于state.blog的blogList)也被更改
const {
blogList,
isLoading
} = this.props;
return (
<div className="App">
{
isLoading
?
<div className="loading">加载中……</div>
:
<ul>
{
blogList.map(blog => {
const {
id,
title,
body
} = blog;
return (
<li key={id}>
<h2>{title}</h2>
<p>{body}</p>
</li>
)
})
}
</ul>
}
</div>
);
}
}
const mapState = state => {
return {
blogList: state.blog.list,
isLoading: state.ux.isLoading
}
}
export default connect(mapState, { requestBlog })(App);
(3)src/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
export default createStore(
rootReducer,
applyMiddleware(thunk)
);
(4) src/actions/actionType.js
export const SHOW_LOADING = 'SHOW_LOADING';
export const HIDE_LOADING = 'HIDE_LOADING';
export const REQUEST_BLOG_SUCCESS = 'REQUEST_BLOG_SUCCESS';
(5) src/actions/ux.js
import {
HIDE_LOADING,
SHOW_LOADING
} from './actionType';
export const showLoading = () => {
return {
type: SHOW_LOADING
}
}
export const hideLoading = () => {
return {
type: HIDE_LOADING
}
}
(6) src/actions/blog.js
import axios from 'axios';
import {
REQUEST_BLOG_SUCCESS
} from './actionType';
import {
showLoading,
hideLoading
} from './ux';
//App.js中的componentDidMount中调用的requestBlog
export const requestBlog = () => {
return (dispatch) => {
// 开始请求之前先显示loading
dispatch(showLoading());
axios.get('http://jsonplaceholder.typicode.com/posts')
.then(resp => {
// 请求返回了数据之后,dispatch一个动作, 并且把返回的值传到这个动作所对应的reducer去处理
dispatch({
type: REQUEST_BLOG_SUCCESS,
// 修改数据
payload: {
list: resp.data
}
})
})
.catch(err => {
// 实际上,这里也应该去做一个dispatch,用于reducer的错误处理
console.log(err);
})
.finally(()=> {
// 请求结束之后,隐藏loading
dispatch(hideLoading())
})
}
}
(7) src/reducers/ux.js
import {
HIDE_LOADING,
SHOW_LOADING
} from '../actions/actionType';
const initialState = {
isLoading: false
}
export default (state=initialState, action) => {
switch(action.type) {
case SHOW_LOADING:
return {
...state,
isLoading: true
};
case HIDE_LOADING:
return {
...state,
isLoading: false
};
default:
return state;
}
}
(8)src/reducers/blog.js
import { REQUEST_BLOG_SUCCESS } from '../actions/actionType';
const initialState = {
list: [{
id: 1,
title: 'niubility',
body: 'h5 1806'
}]
}
export default (state=initialState, action) => {
switch(action.type) {
case REQUEST_BLOG_SUCCESS:
return {
...state,
list: action.payload.list
};
default:
return state;
}
}
(9)src/reducers/index.js
import { combineReducers } from 'redux';
import blog from './blog';
import ux from './ux';
//导出blog.js ux.js
export default combineReducers({
blog,
ux
})
网友评论