npm install --save redux
npm install --save react-redux
npm install --save-dev redux-devtools
應用中全部的 state 都以一個對象樹的形式儲存在一個單一的 store 中。 唯一改變 state 的辦法是觸發 action,一個描述發生什麼的對象。 爲了描述 action 如何改變 state 樹,先編寫 reducers。react
const defaultState = {} export default (state = defaultState,action)=>{ return state }
import { createStore } from 'redux' import reducer from './reducer' const store = createStore(reducer) export default store
組件來獲取 store 中的數據jquery
import store from './store' // ... constructor(props){ super(props) console.log(store.getState()) }
chrome 搜索插件 Redux DevTools 並安裝ios
import { createStore } from 'redux' import reducer from './reducer' const store = createStore(reducer, + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) export default store
Action 是 store 數據的惟一來源。chrome
建立 actionnpm
const action ={ type:'', value: '' } store.dispatch(action)
更改 Reducerredux
export default (state = defaultState,action)=>{ if(action.type === 'changeInput'){ let newState = JSON.parse(JSON.stringify(state)) //深度拷貝state newState.inputValue = action.value return newState } return state }
更新組件數據axios
constructor(props){ // ... storeChange(){ this.setState(store.getState()) } this.storeChange = this.storeChange.bind(this) store.subscribe(this.storeChange) }
使用單獨的模塊或文件來定義 action type 常量並非必須的,甚至根本不須要定義。對於小應用來講,使用字符串作 action type 更方便些。不過,在大型應用中把它們顯式地定義成常量仍是利大於弊的。app
actionTypes.jsdom
const ADD_TODO = 'ADD_TODO'; const REMOVE_TODO = 'REMOVE_TODO'; const LOAD_ARTICLE = 'LOAD_ARTICLE';
組件中引用異步
import { ADD_TODO , REMOVE_TODO , LOAD_ARTICLE } from './store/actionTypes'
相應的 Reducer 也要更改
import { ADD_TODO , REMOVE_TODO , LOAD_ARTICLE } from './store/actionTypes' const defaultState = {} export default (state = defaultState,action)=>{ if(action.type === ADD_TODO){ let newState = JSON.parse(JSON.stringify(state)) newState.inputValue = action.value return newState } // ... return state }
Action 建立函數 就是生成 action 的方法。注意與 action 概念相區分。
function addTodo(text) { return { type: ADD_TODO, text } }
actionCreators.js
function addTodo(text) { return { type: ADD_TODO, text } }
組件中使用
import { addTodo } from './actionCreators'; // ... dispatch(addTodo('Use Redux'))
TodoListUI.js
import React, { Component } from 'react'; class TodoListUi extends Component { render() { return ( <div>123</div> ); } } export default TodoListUi;
TodoList.js
import TodoListUI from './TodoListUI' render() { return ( <TodoListUI /> ); }
TodoListUi 改寫成無狀態組件
import React from 'react'; const TodoListUi = (props)=>{ return( <> some code </> ) } export default TodoListUi;
不過就是走一遍上面的流程
actionCreatores.js
export const getListAction = (data)=>({ type: xxx, data })
組件
import axios from 'axios' import {getListAction} from './store/actionCreatores' componentDidMount(){ axios.get('https:// xxx').then((res)=>{ const data = res.data const action = getListAction(data) store.dispatch(action) }) }
reducer.js
import {GET_LIST} from './actionTypes' const defaultState = { list:[] } export default (state = defaultState,action)=>{ if(action.type === GET_LIST ){ let newState = JSON.parse(JSON.stringify(state)) newState.list = action.data.data.list return newState } return state }
注意不是 react 中間件
npm install --save redux-thunk
import { createStore , applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( reducer, applyMiddleware(thunk) )
因此咱們這樣寫
import { createStore , applyMiddleware ,compose } from 'redux' const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(thunk)) const store = createStore( reducer, enhancer) export default store
在 actionCreators.js 中寫業務
actionCreators.js 都是定義好的 action,根本沒辦法寫業務邏輯,有了Redux-thunk以後,能夠把TodoList.js中的 componentDidMount 業務邏輯放到這裏來編寫。
import axios from 'axios' //... export const getTodoList = () =>{ return (dispatch)=>{ axios.get('https:// xxx ').then((res)=>{ const data = res.data const action = getListAction(data) dispatch(action) }) } }
之前的action是對象,如今的action能夠是函數了,這就是redux-thunk帶來的好處
組件中
import { getTodoList } from './store/actionCreatores' // ... componentDidMount(){ const action = getTodoList() store.dispatch(action) }
安裝
npm install --save redux-saga
store/index.js
import createSagaMiddleware from 'redux-saga' const sagaMiddleware = createSagaMiddleware();
Redux-thunk 替換成 saga
import { createStore , applyMiddleware ,compose } from 'redux' import reducer from './reducer' import createSagaMiddleware from 'redux-saga' const sagaMiddleware = createSagaMiddleware(); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)) const store = createStore( reducer, enhancer) export default store
建立 store/sagas.js
import {takeEvery, put} from 'redux-saga/effects' import {GET_MY_LIST} from './actionTypes' import {getListAction} from './actionCreatores' import axios from 'axios' //generator函數 function* mySaga() { //等待捕獲action yield takeEvery(GET_MY_LIST, getList) } function* getList(){ const res = yield axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList') const action = getListAction(res.data) yield put(action) } export default mySaga;
store/index.js
import { createStore , applyMiddleware ,compose } from 'redux' // 引入createStore方法 import reducer from './reducer' import createSagaMiddleware from 'redux-saga' import mySagas from './sagas' const sagaMiddleware = createSagaMiddleware(); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)) const store = createStore( reducer, enhancer) sagaMiddleware.run(mySagas) export default store
react-redux 不是 redux,
React-Redux 是 Redux 的官方 React 綁定庫。它可以使你的 React 組件從 Redux store 中讀取數據,而且向 store 分發 actions 以更新數據
npm install --save react-redux
import React from 'react'; import ReactDOM from 'react-dom'; import TodoList from './TodoList' import { Provider } from 'react-redux' import store from './store' const App = ( <Provider store={store}> <TodoList /> </Provider> ) ReactDOM.render(App, document.getElementById('root'));
先製做映射關係,映射關係就是把原來的state映射成組件中的props屬性
const stateToProps = (state)=>{ return { inputValue: state.inputValue } }
使用 connect 獲取 store 中的數據
import {connect} from 'react-redux' export default connect(inputValue, null)(TodoList); // 這裏的 inputValue 表明一個映射關係
例子:當咱們修改中的值時,去改變store數據,UI界面也隨之進行改變。
import React, { Component } from 'react'; import store from './store' import { connect } from 'react-redux' class TodoList extends Component { constructor(props){ super(props) this.state = store.getState() } render() { return ( <div> <div> <input value={this.props.inputValue} onChange={this.props.inputChange} /> <button>提交</button> </div> <ul> <li></li> </ul> </div> ); } } const stateToProps = (state)=>{ return { inputValue : state.inputValue } } const dispatchToProps = (dispatch) =>{ return { inputChange(e){ console.log(e.target.value) } } } export default connect(stateToProps,dispatchToProps)(TodoList);
派發 action 到 store 中 (再走一遍流程)
const dispatchToProps = (dispatch) =>{ return { inputChange(e){ let action = { type:'change_input', value:e.target.value } dispatch(action) } } }
reducer
const defalutState = { inputValue : 'jspang', list :[] } export default (state = defalutState,action) =>{ if(action.type === 'change_input'){ let newState = JSON.parse(JSON.stringify(state)) newState.inputValue = action.value return newState } return state }
參考資料
- 嗶哩嗶哩 jspang 的 視頻
- 相關官方文檔