这篇从redux讲起,redux不经常单独使用,而是与react-redux这个绑定库一起完成大量操作,使代码结构更加清晰,最后简单讲解redux-thunk,它是实现异步请求的常用中间件之一。掌握这三个知识点后,就可以实现一个完整react的Demo啦~(redux-saga较复杂,单独放在下次)
Redux
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux 除了和 React 一起用外,还支持其它界面库。
Store
数据仓库,使用createStore
函数,用来生成 Store
import { createStore } from 'redux';
const store = createStore(fn);// fn是一个函数
- 创建仓库store后,在组件内可以使用方法
store.subscribe,store.dispatch,store.getState,store.subscribe
- State: 是Store对象包含的全部数据,如上所述,当前时刻的数据可以用
store.getState
获取
- 获取数据
let state = store.getState()
- 修改数据
store.dispatch({type: "add", content: { msg: '一些数据 '}})
- 监听视图
//监听数据变化,重新渲染页面
store.subscribe(() => {
ReactDOM.render(<Timer></Timer>, document.getElementById('root'))
})
Action
是一个对象,必须有type属性,其余属性皆可自定义,它是 store 数据的唯一来源
const action = {
type: 'ADD_TODO',
text: 'Learn Redux'
};
Action Creator
是生成 action 的方法,可以看出这个函数只是返回了一个简单的action
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
Dispatch
store.dispatch
接受一个 Action 对象作为参数,将它发送出去
store.dispatch({type: "add", content: { msg: '一些数据 '}})
// 或者调用Action Creator
store.dispatch(addTodo('Learn Redux'));
Reducer
一个函数 通过获取动作改变数据,生成一个新的state,从而改变页面 ( reducer会接收到action的信息 并进行state处理),combineReducers
可以把多个reducer函数合并
import * as types from './actionType'
// 可以描述一个初始state
const initState = {
...
}
const reducer = function(state = initState, action) {
switch(action.type) {
// 根据不同的action.type
// type一般大写定义
case types.SET_BUTTON_LOADING: {
let newState = JSON.parse(JSON.stringify(state))
newState.submitdDisabled = action.buttonLoading;
// 生成新的state
return newState
}
case types.CHANGE_TASK_SWITCH: {
let newState = JSON.parse(JSON.stringify(state))
newState.taskSwitch = action.taskSwitch;
return newState;
}
default:
return state
}
Redux流程图
react-redux
- Redux 官方提供的 React 绑定库。 具有高效且灵活的特性
- 使用redux时,我们需要进行大量的手工订阅state的状态变化,再进行对state的修改,在使用
react-reudx
这个工具后,我们只需对store
进行处理,react
组件就会有相应的变化- react-redux的思路是 把store直接集成到React应用的顶层props里面,这样各个子组件就能访问到顶层props了
- 它有两个API :
<Provider store>
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
<Provider store>
- Provider目的是使所有组件都能访问到redux中的数据 (把store和组件中的state进行关联)
- Provider相当于一个顶级组件,将自己的根组件嵌在它之中才能使用connect方法,否则要把 store 作为 props 传递到每一个被 connect() 包装的组件才行
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
root
)
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
connect是用来连接 React 组件与 Redux store(它将store上的getState 和 dispatch 包装成组件的prop)
mapStateToProps(state, [ownProps]): stateProps
-
mapStateToProps是把state映射到props中去 ,这个函数有两个参数,第一个参数如果写了会监听Redux store的变化
-
如果指定了该回调函数中的第二个参数 ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)。
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
- mapDispatchToProp把各种dispatch也变成了props让你可以直接使用(实现了方法的共享)
// 在组件中使用时
// mapStateToProps是把state映射到props中去
// mapDispatchToProp把各种dispatch也变成了props让你可以直接使用
const mapStateToProps = (state) => {
// 必须返回一个纯对象
return { // prop : state.xxx | 意思是将state中的某个数据映射到props中
foo: state.bar
}
} // 渲染的时候就可以使用this.props.foo
const mapDispatchToProps = (dispatch) => {
// 默认传递参数就是dispatch
return { onClick: () => { dispatch({ type: 'increatment' });
}
};
}
// 然后render中直接通过this.props.onClick来调用dispatch,这样子就不需要在代码中来进行store.dispatch了
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
React-redux流程图
Redux-thunk
redux-thunk 是一个比较流行的 redux 异步 action 中间件
使用方法:
store->index.js:
import { createStore, applyMiddleware } from 'redux' // 引入 createStore方法
import thunk from 'redux-thunk'; // 引入redux-thunk
import reducer from './reducer' // 引入reducer
const store = createStore(reducer, applyMiddleware(thunk)) // 创建数据存储仓库
export default store // 将仓库暴露出去
组件内使用mapDispatchToProps
dispatch
方法与之前相同,reducer
的写法也相同
store->actionCreate.js中生成action
时:
- 首先这个函数使用
dispatch
作为参数(这解决了dispatch
不好获取的问题);当redux-thunk
发现组件内dispatch
了一个函数,它就会传递一个dispatch
参数,而不会传递给reducer
,防止reducer
遇到一个函数而不知所措。 - 函数内可以使用一些异步方法,比如
axios
或者fetch
,处理完之后触发一个dispatch
,使它返回一个简单的同步action
即可。
如下例:
const getId = (user_group_id) => {
return (dispatch) => {
axios.get('.../detail', {
params: {
user_group_id
}
})
.then((response) => {
const data = response.data.result_data.group_info
// console.log(data)
dispatch(getDetail(data))
})
.catch(() => {
console.log('catch')
})
}
}
function getDetail (data) {
return {
type: GET_DETAIL,
data
}
};