某年某月某日,某师兄说:学一个东西,不能只停留在表面,只知道怎么用是完全不够的,
要清楚的明白,为什么这么做,为什么不那样做,还得从源码开始,虽然起步可能会比较坎坷,毕竟知识储备有限。
点到为止了,所以我也就尝试去看了 redux 以及 react-redux 源码,确实坎坷。
在此对看过的一些资料做一个总结。
先讲讲其基本用法,最后附上源码解析:
其实简单的应用,甚至只是一个单页应用,那完全是用不到 redux 的,只会让代码量提升了却没什么好处,出力不讨好。
redux 不仅仅只限于和 react 搭配使用,它可说是一个数据(state)管理器,也可在其他场景中使用。
当然 react-redux 就顾名思义不是适用于任何场景了。
如果用过 FLUX 的同学,应该会对 redux 的主要思想比较容易理解:
单项数据流,但存在与 FLUX比较大的差别是redux 整个应用只有一个数据源,也就是只有一个 store,在复杂的应用中也统一管理所有的数据。
我觉得 store 是整个 redux 的核心,最为核心的就是 store 的四个 function:
dispatch:分发 Action 到对应的 Reducer后,根据 Reducer 逻辑更改store 中的 state,之后触发 subscribe的 listener,
getState:获取当前store 中的 state 数据,
subscribe: 注册 listener,在 state 变化时触发,
replaceReducer:替换 Reducer,修改 state 变化逻辑,不是很常用。
我们需要做的是创建 store,Action,Reducer,最基本,最简单的写法:
//actionType
export const ACTION_TYPE = 'ACTION_TYPE';
//actionCreator
let actionCreator = (config) => {
return {
type: ACTION_TYPE, // 必须定义 type
config // 可定义任何属性,都会传递到 reducer,用于修改 state
}
}
action 其实就是一个普通对象,当然你也可以直接写这个对象,但存在诸多劣势。
import { ACTION_A, ACTION_B } from '../actions';
let initialState = { ... }
function example(state = initialState, action) {
switch(action.type) {
case ACTION_A:
return Object.assign({}, state, action.config)
case ACTION_B:
return Object.assign({}, state, action.config)
default:
return state
}
}
reducer 里只做修改state,纯函数。
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk';
import reducers from './reducers';
let store = createStore(reducers);
ReactDOM.render((
<Provider store={store}>
// ...
</Provider>
), document.querySelector('#app'));
创建一个 store,也是整个应用唯一的 store。
Provider 是 react-redux 中提供的。可以通过Provider将 store 传递到包含在它之内的所有子组件里,但需配合 connect 使用。
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actionA } from 'actions';
class ComponentA extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
let { dispatch } = this.props;
dispatch(actionA())
}
render() {
//由于所用到的 state 都绑定到了 props 中,component 中用到的时候可在 props 中获取
<div>{this.props.propA}</div>
}
}let mapStateToProps = (state) => {
// attention,会把最新的 state 数据绑定到组件 props 中。
//只需绑定和本组件相关的 state 即可(state 是包含了整个应用的所有数据的。
let { reducerA, reducerB } = state;
return {
propA: reducerA.propA,
propB: reducerB.propB
}
};
export default connect(mapStateToProps)(ComponentA);
connect 也是 react-redux 中提供的,这里简单的介绍下,最基础的用法就是只传mapStateToProps 方法,
返回的是一个包含了调用 getState,subscribe 的原组件(这里即 ComponentA)。
这样就完成一整套简单的 redux 融合的 react 组件应用了。
但这样的数据交互都是同步的,无法支持 ajax 的异步请求。
如果想实现就需要加入 thunk中间件去完成,同时需改造 action,不仅仅是一个简单的普通 Object 即可完成的了。
另一种方法即在 component 中创建store,在 component 内就能直接调用 store.dispatch,store.subscribe,store.getState 等方法,完成数据管理。
但这种写法有个坑,即比如在 componentDidMount中注册了 subscribe 的 listener 时会返回一个 unsubscribe 方法,
用于解绑的,必须在 componentWillUnmount 的时候执行该方法,不然会报警告,在组件卸载时仍在监听。
componentDidMount(){
self.unsubscribe = subscribe(()=>{xxx});
}
componentWillUnmount(){
self.unsubscribe();
}
需要异步请求时,需要对 store 和 action 进行改造加强:
export default function configureStore(initialState) {
const store = createStore(rootReducer,initialState,applyMiddleware(
thunkMiddleware,//支持异步操作
createLogger()//输出 redux 的action 和 state相关 log
))
return store;
}
action 需改成根据异步请求不同状态发出不同的 action,包装成一个方法来完成export function fetchAction() {
return dispatch => {
dispatch({
type: 'REQUEST_POSTS'
})
return fetch(`xxxxx`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(function (json) {
if (json.success) {
dispatch({
type: CATEGORIES_RECEIVE_POSTS,
data: json
})
} else {
info('数据获取失败,请稍候再试!')
}
})
.catch(e => {
debugger;
})
}
}
直白的理解就是发起请求时发出一个 action(不真实改变 state,只改变请求状态),
请求返回成功一个action,失败一个 action 让 reducer 做出不同的对 state 的修改,即可完成异步请求。
源码解析接下一篇博客哟~