设计思想
1.web应用是一个状态机,试图与状态是一一对应的.
2.所有的状态,保存在一个对象里.
基本改变和API
1.Store
Store就是保存数据的地方,你可以把它看成一个容器,整个应用只能有一个Store.
Redux提供createStore这个函数,用来生成Store.
import { createStore } from 'redux'; const store = createStore(reducer); createStore函数接受另一个reducer函数作为参数,返回新生成的Store对象.
2.State
State对象包含所有数据,如果想得到某个时点的数据,就要对Store生成快照,这种时点的数据集合,
就叫做State.当前时刻的State,可以通过store.getState()拿到.
const state = store.getState();
3.Action
state的变化,会导致view的变化,但是,用户接触不到state,只能接触到view,所以.stated的变化必须是view导致的.Action就是view发出的通知,表示state应该要发生变化了.
Action是一个对象,其中的type属性是必须的,表示Action的名称,其他属性可以自由设置.
const action = { type: 'ADD_TODO',payload: 'learn Redux' };
Action的名称是ADD_TODO,它携带的信息是字符串Learn Redux.
可以这样理解,Action描述当前发生的事情,改变State的唯一方法,就是使用Action,它会运送数据到Store.
4.Action Creator
View要发送多少种信息,就会有多少种Action,可以定义一个函数生成Action,这个函数就叫ActionCreator.
const ADD_TODO = '添加 TODO'; function addToDo(text) { return { type: ADD_TODO,text } }
5.store.dispatch()
store.dispatch()是view发出Action的唯一方法.
store.dispatch(addTODO('Learn Redux'));
6.reducer
store收到Action以后,必须给出一个新的State,这样view才会发生变化.
这种State的计算过程就叫做Reducer.
Reducer是一个函数,他接受Action和当前State作为参数,返回一个新的State.
export default (state = 0,action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; case 'getSource': return 'bbbb'; default: return 0; } }
reducer函数收到名为ADD的action以后,就返回一个新的state,作为加法的计算结果,其他计算的逻辑,
也可以根据Action的不同来实现.
实际应用中,Reducer函数不用像上面这样手动调用,store.dispatch方法会触发Reducer的自动执行.
为此,Store需要知道Reducer函数,做法就是生成Store的时候,将Reducer传入createStore方法.
const store = createStore(reducer);
createStore接受Reducer作为参数,生成一个新的Store.以后每当store.dispatch发送过来一个新的Action,
就会自动调用Reducer,得到新的State.
7.纯函数
Reducer函数最重要的特征是,它是一个纯函数,也就是说,只要是同样的输入,必须得到同样的输出.
由于Reducer是纯函数,就可以保证同样的State,必定得到同样的view,但正因为这一点,Reducer函数里面不能改变
State,必须返回一个全新的对象.
return Object.assign({},state,{ thingtochange }) export default (state = 0,action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; case 'getSource': return 'bbbb'; case 'ajax': return action.data; case 'ajaxError': return action.data default: return 0; } }
8.store.subscribe()
Store允许使用store.subscribe方法设置监听函数,一旦State发生变化,就自定执行这个函数.
store.subscribe(listener);
显然,只要把view的更新函数(render或this.setState)放入listen,就会实现view的自动渲染.
let unsubscribe= store.subscribe(() => console.log(store.getState()));
unsubscribe();
Store的实现
1.store.getState(),store.dispatch(),store.subscribe()
let { subscribe,dispatch,getState } = createStore(reducer);
Reducer的拆分
Reducer函数负责生成State,由于整个应用只有一个state对象,包含所有数据,对于大型应用来说,这个State
必然十分庞大,导致Reducer函数也十分庞大.
Redux提供了一个combineReducers方法,用于Reducer的拆分,你只要定义各个子Render函数,
然后用这个方法,将它们合成一个大的Reducer.
import { combineReducers } from 'redux'; const chatReducer = combineReducers({ chatLog,statusMessage,userName })
工作流程
1.用户发出Action
store.dispatch(action);
2.Store自动调用Reducer,并且传入两个参数,当前State和收到的Action.Reducer会返回新的State.
let nextState = todoApp(prevIoUsState,action);
store.subscribe(listener);
4.listener可以通过store.getState()得到当前状态,如果使用的是React,这时可以触发重新渲染view.
function listerner() { let newState = store.getState(); component.setState(newState); }
实例
import React,{ Component } from 'react'; import { render } from 'react-dom'; import reducer from '../reducers/reducer.js'; import { createStore } from 'redux'; const store = createStore(reducer); class App extends Component { constructor(props) { super(props); this.state = { count: 0,source: 'aaaa',ajaxSource: 'ajax' }; } handleAdd = () => { store.dispatch({ type: 'INCREMENT' }); } handleDel = () => { store.dispatch({ type: 'DECREMENT' }); } handleGet = () => { store.dispatch({ type: 'getSource' }) } handleAjax = () => { fetch('../api/response.json') .then(response => response.json()) .then((res) => { store.dispatch({ type: 'ajax',data: res }); }).catch((err) => { store.dispatch({ type: 'ajaxError',data: err }); }) } render() { let _this = this; store.subscribe(() => { let o = store.getState(); _this.setState({ [o.type]: store.getState()[o.type] }) }); return ( <div> <span>{ this.state.count }</span> <button onClick={ this.handleAdd }>add</button> <button onClick={ this.handleDel }>del</button> <span>{ this.state.source }</span> <button onClick={ this.handleGet }>获取数据</button> <span>{ this.state.ajaxSource.res }</span> <button onClick={ this.handleAjax }>获取ajax数据</button> </div> ); } } render(<App />,document.getElementById('root'));
export default (state = 0,action) => { switch (action.type) { case 'INCREMENT': return Object.assign({},{ count: state + 1,type: 'count' }); case 'DECREMENT': return Object.assign({},{ count: state - 1,type: 'count' }); case 'getSource': return Object.assign({},{ source: action.dada,type: 'source' }); case 'ajax': return Object.assign({},{ ajaxSource: action.data,type: 'ajaxSource' }) case 'ajaxError': return Object.assign({},type: 'ajaxSource' }); default: return state } }