react中用redux进行状态管理
文件目录结构
|-- README.md
|-- package.json
|-- public
| `-- index.html
|-- src
| |-- components // 放UI组件,即代码不含redux内容
| | `-- counter.js
| |-- containers // 放容器组件,即代码含redux内容
| | `-- App.js
| |-- index.js
| `-- redux
| |-- action-types.js
| |-- actions.js
| |-- reducers.js
| `-- store.js
`-- yarn.lock
需要使用的库和插件
redux
- 下载
npm install --save redux
- 使用场景
store.js
import {createStore, applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; import {counter} from './reducers'; export const store = createStore( counter, applyMiddleware(thunk) );
createStore
用于创建store
对象,store
的作用是管理state
,action
和reducer
applyMiddleware
用于应用中间件,如上所示,代码使用了thunk
中间件,使redux
可以编写异步代码
reducers.js
import {combineReducers} from 'redux'; function counter(state = 0, action) { ··· ··· } function counter2(state = 0, action) { ··· ··· } export default combineReducers({ counter, counter2 }) // redux向外暴露的state是对象结构 // {counter: counter的state, counter2: counter2的state}
- 当
reducers.js
只有一个reducer
对象,则可以不使用combineReducers
方法,直接export default
reducer对象即可
react-redux
-
下载
npm install --save react-redux
-
使用场景
index.js
··· import {Provider} from 'react-redux'; import {store} from "./redux/store"; ··· ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root') );
store
对象以属性的形式传给Provider
组件
app.js
import {connect} from 'react-redux'; import {Counter} from '../components/Counter'; import {increment, decrement, incrementAsync} from '../redux/actions'; export default connect( state => ({count: state}), {increment, decrement, incrementAsync} )(Counter);
- 将react和redux进行连接,通过
connect
将state
和action
以属性的形式传递给Counter
组件
redux-thunk
- 下载
npm install --save redux-thunk
- 使用场景
store.js
import {createStore, applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; import {counter} from './reducers'; export const store = createStore( counter, applyMiddleware(thunk) );
thunk
以中间件的形式添加到store
对象中,使原本不支持异步的redux支持异步代码
prop-types
(可选)
- 下载
npm install --save prop-types
- 使用场景
Counter.js
export class Counter extends Component{ static propTypes = { count: PropTypes.number.isRequired, increment: PropTypes.func.isRequired, decrement: PropTypes.func.isRequired, incrementAsync: PropTypes.func.isRequired } ··· render(){ return ( ··· ) }
- 非必要步骤,但是添加
propTypes
事先声明props
中含有的属性值,更加严谨
redux-devtools-extension
(用于redux调试)
- 下载
npm install --save-dev redux-devtools-extension
- 使用场景
store.js
import {composeWithDevTools} from 'redux-devtools-extension'; ······ export const store = createStore( counter, composeWithDevTools(applyMiddleware(thunk)) );
- 需要配合Chrome插件
ReduxDevTools
进行使用
src/
下各文件的内容
src/redux/action-types.js
- 存放固定的常量信息,在需要的文件中进行引用,以防拼写出错
export const INCREMENT = "increment"; export const DECREMENT = "decrement";
src/redux/actions.js
- 存放生成
action
的工厂函数,同步代码返回对象,异步代码返回函数(前提是使用上了redux-thunk
)// 从action-types中引入常量 import {INCREMENT, DECREMENT} from "./action-types"; // 同步代码,返回对象 export const increment = (data) => ({type: INCREMENT, data}); export const decrement = (data) => ({type: DECREMENT, data}); // 异步代码,返回函数 export const incrementAsync = (data) => { return dispatch => { setTimeout(()=>{dispatch(increment(data))} ,1000) } };
src/redux/reducers.js
- 存放
reducer
,用于在store.js
文件中引用并生成store
对象import {combineReducers} from 'redux'; // 从action-types中引入常量 import {INCREMENT, DECREMENT} from "./action-types"; export function counter(state=0, action) { switch (action.type) { case INCREMENT: return state + action.data; case DECREMENT: return state - action.data; default: return state } } export default combineReducers({ counter })
src/redux/store.js
- 生成
store
对象,并向外暴露给index.js
import {createStore, applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; import {composeWithDevTools} from 'redux-devtools-extension'; import reducers from './reducers'; export const store = createStore( reducers, composeWithDevTools(applyMiddleware(thunk)) );
src/components/Counter.js
- UI组件
import React, {Component} from 'react'; import {PropTypes} from 'prop-types'; export class Counter extends Component{ static propTypes = { count: PropTypes.number.isRequired, increment: PropTypes.func.isRequired, decrement: PropTypes.func.isRequired, incrementAsync: PropTypes.func.isRequired } increment = () => { const val = this.select.value*1; this.props.increment(val) } decrement = () => { const val = this.select.value*1; this.props.decrement(val) } incrementAsync = () => { const val = this.select.value*1; this.props.incrementAsync(val) } render() { const count = this.props.count; return ( <div> <p>click {count} times</p> <div> <select ref={select => this.select = select}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementAsync}>incrementAsync</button> </div> </div> ); } }
src/containers/App.js
- 容器组件,由于
reducers.js
使用了combineReducers
,所以connect
函数中要通过state.counter
指定使用哪个state
;若reducers.js
只有一个reducer
且没有使用combineReducers
,则以下代码的state.counter
改成state
即可import {connect} from 'react-redux'; import {Counter} from '../components/Counter'; import {increment, decrement, incrementAsync} from '../redux/actions'; export default connect( state => ({count: state.counter}), // mapStateToprops {increment, decrement, incrementAsync} // mapDispatchToProps )(Counter);
总结
|-- README.md
|-- package.json
|-- public
| `-- index.html
|-- src
| |-- components // 放UI组件,即代码不含redux内容
| | `-- counter.js // 不需要使用redux相关库
| |-- containers // 放容器组件,即代码含redux内容
| | `-- App.js // 使用react-redux({connect})
| |-- index.js // 使用react-redux({Provider})
| `-- redux
| |-- action-types.js
| |-- actions.js
| |-- reducers.js // 使用redux({combineReducers})
| `-- store.js // 使用redux({createStore, applyMiddleware}), redux-thunk(thunk), redux-devtools-extension({composeWithDevTools})
`-- yarn.lock