redux概述
redux是一个状态管理器。在react中使用redux可以帮我们实现全局的数据管理。这篇文章选择了最简单的计数器为例子,通过纯redux的使用, react跟redux配合使用、以及异步redux从渐到深介绍redux。
首先,我们要先理清楚基础的概念以及工作流程。我们需要理解的地方就是:
store.dispatch(action) => excute reducer => change state
理解store
store在我理解,它是一个管理者的概念。
1.创建store: 我们可以用combineReducers把所有的reducer传进去作为参数,当然也可以只传一个reducer
const store = createStore(
combineReducers({
todos,
counter
})
);
2.dispatch: 通过store.dispatch(action)可以触发对应的reducer(这里的对应的,主要依赖于reducer里面对action的type进行判断)
3.subscribe: 通过store.subscribe可以监听state的变化。
store.subscribe(() => console.log(store.getState()));
理解reducer
reducer,它是一个修改state的函数。这里的修改,不是修改某个属性值,而是直接返回一个新的state。可以用我们的...,也可以用Object.assign。它主要负责根据action.type,修改不同的state属性值。
const initialState = { count: 0 };
const counter = (state = initialState, action) => {
switch (action.type) {
case "CUSTOM_COUNT":
return {
count: state.count + action.count
};
default:
break;
}
};
理解actions
actions就是一个纯函数。在同步的action里面,它负责返回一个对象,给dispatch执行。
function addOne() {
return {type: 'ADD', count: 1}
}
用一个计数器的例子帮你理解redux
reudx开发流程
我们写代码的时候,
首先确定有多少种actions,这里建议定义一个actionsType.js的文件。
然后写reducer,在reducer引入actionsType.js, 里面进行switch的时候,写成actionsType.XXX以避免拼写错误问题。
然后combine reducer,创建store
最后,写actions,引入actionsType.js,这里面的函数就只是返回一个对象的纯函数,这个对象有个type,就用actionsType.XXX,你愿意返回什么值就返回什么值,跟reducer对的上就好啦。
伪代码:
actionsType.js
export default () => (
{
ADD_ONE: 'ADD_ONE'
}
)
reducer
import actionsTypes from "./actionsType.js"
const reducer1 = (state = {}, action) => {
switch (action.tyep) {
case actionsTypes.ADD_ONE:
return {...state, count: action.count}
default:
return state;
}
}
actions
import actionsTypes from "./actionsType.js"
function addOne() {
return {actionsTypes.ADD_ONE, count: 1} // 上面的reducer取的就是action.count,就是因为我们这里给它的是这样的。
}
store
const store = createStore(
combineReducers({
reducer1
})
);
store.dispatch(addOne())// 注意dispatch的是一个对象,所以是action执行后的结果
store.suscibe(() => console.log(store.reducer1.getState()))// 如果创建的时候不是combineReducers就可以直接store.getState()
下面的例子为了便于理解,就没有分那么多个文件了。
pureRedux
直接上代码,因为具体步骤解释上面的伪代码应该已经写清楚了。
import {
createStore,
combineReducers,
bindActionCreators
} from "redux";
import React from "react"
function run () {
const initialState = { count: 0 };
// reducer
function counter (state = initialState, action) {
switch (action.type) {
case 'ADD':
return { count: state.count + action.count} // create a new state, rather than just change the state.count
case 'REDUCE':
return { count: state.count - action.count}
default:
return state
}
}
const store = createStore(combineReducers({counter}))
store.subscribe(() => {
console.log(store.getState())
})
//action
function addOne() {
return {type: 'ADD', count: 1}
}
function reduceOne() {
return {type: 'REDUCE', count: 1}
}
// trigger
store.dispatch(addOne()) // dispatch the excute result of action
// in another way to trigger
const dispatchReduceOne = bindActionCreators(reduceOne, store.dispatch)
dispatchReduceOne()
}
export default () => (
<div>
<button onClick={run}>Run</button>
<p>* 请打开控制台查看运行结果</p>
</div>
);
在react中使用redux
在react中使用redux,我们不会再用store.subscribe去监听值的变化,而是用react-reudx里面的connect方法,把我们要监听的值和要调用的action方法作为属性值传给组件。
connect方法,它是一个返回高阶组件的高阶组件。先看第一层,connect(mapStateToProps, mapDispatchToProps)返回我们可以在组件里面使用到的state值和dispatch方法。第二层就是把我们的组件传递进去,让它具备这些值跟方法。
这里有个需要讲解的地方就是bindActionCreators,它负责把actions跟dispatch绑定在一起,返回一个新的函数。这个新的函数,直接执行就相当于store.dispatch。
看源码:
function bindActionCreator()actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments))
}
}
import {
createStore,
combineReducers,
bindActionCreators
} from "redux";
import React from "react"
import { connect, Provider } from "react-redux";
const initialState = { count: 0 };
function counter (state = initialState, action) { // reducer
switch (action.type) {
case 'ADD':
return { count: state.count + action.count} // create a new state, rather than just change the state.count
case 'REDUCE':
return { count: state.count - action.count}
default:
return state
}
return state
}
const store = createStore(combineReducers({counter}))
//action
function addCount(count) {
return {type: 'ADD', count}
}
function reduceCount(count) {
return {type: 'REDUCE', count}
}
class counterElement extends React.Component {
render () {
const { count, addCount, reduceCount } = this.props
return <Provider store={store}>
<div>
<p>the count is: {count}</p>
<input type="button" value="add" onClick={() => addCount(5)}/>
<input type="button" value="reduce" onClick={() => reduceCount(5)}/>
</div>
</Provider>
}
}
function mapStateToProps (state) {
console.log(state)
return {
count: state.counter.count
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({addCount, reduceCount}, dispatch)
}
const WrappedCounterElement = connect(mapStateToProps, mapDispatchToProps)(counterElement)
class counterContainer extends React.Component {
render () {
return <Provider store={store}>
<WrappedCounterElement/>
</Provider>
}
}
export default counterContainer
异步redux
如果在action里面调用的方法是异步的,那么是需要通过redux中间件,先把这个action拦截了,等到它完成了,再dispatch出去。
这里主要是我们在创建store的时候,要使用thunk中间件。
const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware, // 允许我们 dispatch() 函数
loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
)
)
import React from "react"
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
function run () {
const loggerMiddleware = createLogger()
const actionsType = {
REQUEST_POSTS: 'REQUEST_POSTS',
RECEIVE_POSTS: 'RECEIVE_POSTS',
INVALIDATE_SUBREDDIT: 'INVALIDATE_SUBREDDIT'
}
//reducer
const initialState = {
isFetching: false,
didInvalidate: false,
items: []
}
function posts(
state = initialState,
action
) {
switch (action.type) {
case actionsType.INVALIDATE_SUBREDDIT:
return Object.assign({}, state, {
didInvalidate: true
})
case actionsType.REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case actionsType.RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
const rootReducer = combineReducers({
posts
})
// store
const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware, // 允许我们 dispatch() 函数
loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
)
)
// action
function requestPosts(subreddit) {
return {
type: actionsType.REQUEST_POSTS,
subreddit
}
}
function receivePosts(subreddit, json) {
return {
type: actionsType.RECEIVE_POSTS,
subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
function fetchPosts(subreddit) {
return function (dispatch) {
dispatch(requestPosts(subreddit)) // display the loading status
return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json =>
dispatch(receivePosts(subreddit, json))
)
}
}
// excute
store.dispatch(fetchPosts('reactjs')).then(() => console.log(store.getState()))
}
export default () => (
<div>
<button onClick={run}>Run</button>
<p>* 请打开控制台查看运行结果</p>
</div>
);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。