React Redux
Redux 基础知识
Redux 原则
- 唯一数据源:Redux 应用只维护一个全局状态对象;
- 状态只读:不能直接修改应用的状态,必须通过 action 来实现修改;
- 状态改变通过纯函数完成:reducer 必须是一个纯函数,接收到 action 后也不能直接修改状态,而是要返回一个新的状态,且 reducer 不能有副作用(比如:实时获取时间戳)
Redux 组成
redux 主要由 3 个部分组成:action、reducer、store,一张图揭示它们之间的关系:
action
action 是 store 唯一的数据来源,通过dispatch
方法传递给 store。
actionCreator
action 是一个对象,由 type 和 data 属性组成,一般会定义一个创建 action 对象的函数并暴露出去,统一写在 actionCreators.js 内:
// actionCreators.js
export const getPostList = data =>({
type:'post/get_post_list',
data
})
actionTypes
一般 action 中的 type 属性会用一个专门的文件 actionTypes.js 来管理,防止 action 和 reducer 中的 type 属性不一致,例如:
// actionTypes.js
export const GET_POST_LIST = "post/get_post_list";
export const REMOVE_FIRST_ITEM = "post/remove_first_item";
export const REMOVE_LAST_ITEM = "post/remove_last_item";
那么上面的 getPostList 就可以修改为:
import { GET_POST_LIST } from './actionTypes.js';
export const getPostList = data =>({
type: GET_POST_LIST,
data
})
dispatch
通知 store 需要用到 dispatch
方法,dispatch 可以接收 action 对象,接收后又传给了 reducer,这个过程是同步的,如果我们想要异步处理,比如:从服务器异步请求数据,单纯的 redux 是不行的,需要用到 redux-thunk 插件。
同步 dispatch:
// App.jsx
import { getPostList } from './actionCreators.js';
class App extends Component{
const handleClick=()=>{
this.store.dispatch( getPostList( {id:'123'} ) );
}
render(){
return <div onClick={handleClick}></div>
}
}
异步 dispatch:
// App.jsx
import {demoActionFunc} from 'actionTypes';
class App extends Component{
const handleClick=()=>{
this.store.dispatch( postFilter() );
}
render(){
return <div onClick={handleClick}></div>
}
}
// actionCreators.js
export const getPostList = data =>({
type:'post/get_post_list',
data
})
export const postFilter = () =>{
return dispatch =>{
// 先运行AJAX请求,后执行dispatch
ajax().then(( res )=>{
dispatch( getPostList(res) )
});
}
}
reducer
reducer 用于对传入的 action 根据其 type 值作出响应,然后返回一个新的 state 对象,举个例子:
import * as actionTypes from "./actionTypes";
const defaultState = {
page: 0,
perPage: 10
};
export default (state = defaultState, action) => {
switch(action.type){
case 'demoActionType':
return ...
default:
return state
}
}
一个项目中会有多个 reducer,一般常用 combineReducer 将他们汇总起来,一并传入 createStore 方法内,用于创建 store:
import { combineReducers } from 'redux';
const reducers = combineReducers({ reducer1, reducer2 });
store
store是连接 action 和 reducer 的中间人,一个应用只会存在一个 store,保存着应用的状态。
store 通过createStore(reducers,initalStore)
方法生成,下面是包含 redux-thunk 的例子:
import { createStore, compose, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducers from "./reducers";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));
export default store;
store 的 3 个 API:
.getState()
用来访问 store 中的状态 state
.dispatch(action)
用来发送更新状态意图
.subscribe(listenerFunction)
用来监听状态的变化
function handleChange() { // do something } const unsubscribe = store.subscribe(handleChange) unsubscribe()
使用示例
import { connect, bindActionCreators } from "react-redux";
// 第二个可选参数ownProps代表组件本身的props,如果传入,则当props变化时,mapStateToPorps就会被调用
// 适合state与props有关联的时候使用
const mapStateToProps = (state, ownProps) => {
return {
todos: state.todos
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
increase: action.increase, // action是import进来的actionCreators的合集对象
decrease: action.decrease
}, dispatch);
}
// bindActionCreators()就相当于
// {
// increase: (...args) => dispatch(actions.increase(...args)),
// decrease: (...args) => dispatch(actions.decrease(...args))
// }
const VisibleTodoList = connect(
// 建立store的state与UI组件的props间的映射关系
// 如果省略mapStateToProps,那么UI组件就不会订阅store,即store的更新不会引起UI组件的更新
mapStateToProps,
// 用于建立store.dispatch方法和UI组件的props间的映射关系
mapDispatchToProps
)(TodoList);
React Redux