[阮一峰]
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
Redux 的适用场景:多交互、多数据源
1用户的使用方式复杂
2.不同身份的用户有不同的使用方式(比如普通用户和管理员)
3.多个用户之间可以协作
4.与服务器大量交互,或者使用了 WebSocket
5.View要从多个来源获取数据
从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。
1.某个组件的状态,需要共享
2.某个状态需要在任何地方都可以拿到
3.一个组件需要改变全局状态
4.一个组件需要改变另一个组件的状态
发生上面情况时,如果不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快就会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改变状态、传播状态的变化。
在react 项目终端安装Redux
npm i redux react-redux redux-thunk
<Provider> 组件
Provider组件的出现是作为中间件降低各组件与Store之间的耦合。
connect
方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state
对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state
传下去就很麻烦。
React-Redux 提供Provider
组件,可以让容器组件拿到state
。
首先,对原组件进行了封装: render方法中, 渲染了其子级元素, 使整个应用成为Provider的子组件。
import { Provider} from 'react-redux';
<Provider store={store}>
用Redux容器将状态保存起来
</Provider>

第一步触发action 操作
import { fetchPosts } from '../actions/postActions';
componentDidMount() {
this.props.fetchPosts();
}
第二部进行分发操作
异步操作至少要送出两个 Action:用户触发第一个 Action,这个跟同步操作一样,
没有问题;如何才能在操作结束时,系统自动送出第二个 Action 呢?
奥妙就在 Action Creator
之中。
异步组件:
加载成功后(componentDidMount方法),
它送出了(dispatch方法)一个 Action,向服务器要求数据
这里的fetchPosts
就是 Action Creator
。
fetchPosts
是一个Action Creator(动作生成器)
export const FETCH_POSTS = "FETCH_POSTS";
代码优化es6格式
import {
FETCH_POSTS,
} from './types';
export const fetchPosts = () => dispatch => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(posts =>
dispatch({
type: FETCH_POSTS,
payload: posts
})
)
}
分发操作成功后,将dispatch和数据递交到store
中,store得到action,
第三步:Store
store得到的action 传给postReducer,reducer得到状态,会进行判断到底执行的是哪一个类型
Store
就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store
。
Redux 提供createStore这个函数,用来生成 Store。
import { createStore } from 'redux'
const store = createStore(fn);
createStore
方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了
中间件
Reducer
在异步操作结束后自动执行 ->用到中间件middleware
createStore
方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。
(1.)createStore
方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware
就是第三个参数了
const store = createStore(
reducer,
initial_state,
applyMiddleware(logger)
);
中间件的次序有讲究。
const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger)
);
applyMiddlewares()
applyMiddleware
方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确。
applyMiddlewares()
:它是 Redux
的原生方法,作用是将所有中间件组成一个数组,依次执行
中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法。
redux-thunk 中间件
异步操作至少要送出两个 Action:用户触发第一个 Action
,这个跟同步操作一样,没有问题;如何才能在操作结束时,系统自动送出第二个 Action 呢?
奥妙就在 Action Creator 之中。
加载成功后(componentDidMount
方法),它送出了(dispatch方法)一个 Action,向服务器要求数据 fetchPosts
(selectedSubreddit)。这里的fetchPosts就是 Action Creator
。
下面就是fetchPosts
的代码,关键之处就在里面
export const fetchPosts = () => dispatch => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(posts =>
dispatch({
type: FETCH_POSTS,
payload: posts
})
)
}
这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch
方法发送的。而store.dispatch
方法正常情况下,参数只能是对象,不能是函数。
这时,就要使用中间件redux-thunk
。
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
// Note: this API requires redux@>=3.1.0
const store = createStore(
reducer,
applyMiddleware(thunk)
);
上面代码使用redux-thunk
中间件,改造store.dispatch,使得后者可以接受函数作为参数。
因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator
,然后使用redux-thunk
中间件改造store.dispatch
import { createStore, applyMiddleware,compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducers from './reducers/index';
const initialStore = {};
const middleware = [thunk];
export const store = createStore(
rootReducers,
initialStore,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
)
第四步Reducers
Store
收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer
。
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
const reducer = function (state, action) {
// ...
return new_state;
};
import {
FETCH_POSTS,
} from '../actions/types';
const inittialState = {
items: [],
}
export default function (state = inittialState, action) {
console.log("reducers")
switch (action.type) {
case FETCH_POSTS:
return {
...state, //由actions中的postActions type返回新数据
items: action.payload //将得到最新的状态给
}
default:
return state;
}
}
将最新的状态给posts
组件
import {combineReducers} from 'redux';
import postReducer from './postReducer';
export default combineReducers({
posts1:postReducer
})
Redux 提供了一个combineReducers
方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
你只要定义各个子 Reducer
函数,然后用这个方法,将它们合成一个大的 Reducer。
这种写法有一个前提,就是 State
的属性名必须与子 Reducer
同名
最后一步,将最新的state 返回给Posts 组件
connect()
React-Redux 提供connect()
方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
mapStateToProps()
获得reducers返回的最新状态
//第五步:
const mapStateToProps = state => ({
posts: state.posts1.items
})
mapDispatchToProps()
mapDispatchToProps
是connect
函数的第二个参数,用来建立 UI 组件的参数到store.dispatch
方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps
是一个函数,会得到dispatch
和ownProps(容器组件的props对象)两个参数
import PropTypes from 'prop-types';
//定义类型
Posts.propTypes = {
fetchPosts: PropTypes.func.isRequired,
posts:PropTypes.array.isRequired
}
connect() 的第二个参数mapDispatchToProps()=>fetchPosts
import { fetchPosts } from '../actions/postActions';
//第五步:获得reducers返回的最新状态
const mapStateToProps = state => ({
posts: state.posts1.items,
})
export default connect(mapStateToProps, { fetchPosts })(Posts) ;
网友评论