本教程假设你有React 和 ES6/2015经验。首先从没有使用Redux最简单情况开始,演示到使用Redux从无到有的变化过程,从对比中体会Redux好处。
首先创建一个React组件components/ItemList.js用来抓取和显示条目列表。设置这个静态组件带有状态包含各种items输出,2个boolean状态表示在抓取加载时正常和出错的结果。
import React, { Component } from 'react'; class ItemList extends Component { constructor() { super(); this.state = { items: [ { id: 1, label: 'List item 1' }, { id: 2, label: 'List item 2' }, { id: 3, label: 'List item 3' }, { id: 4, label: 'List item 4' } ], hasErrored: false, isLoading: false }; } render() { if (this.state.hasErrored) { return <p>Sorry! There was an error loading the items</p>; } if (this.state.isLoading) { return <p>Loading…</p>; } return ( <ul> {this.state.items.map((item) => ( <li key={item.id}> {item.label} </li> ))} </ul> ); } } export default ItemList;
|
当渲染时,这个组件应该输出4个条目列表,但是如果设置 isLoading或hasErrored为true时,也会有相应状态输出。
假设我们从 JSON API抓取items,使用Fetch API实现异步请求,它比传统的XMLHttpRequest更方便,能够返回一个响应的promise,Fetch并不适合所有浏览器,需要加入你项目依赖:
npm install whatwg-fetch --save
抓取逻辑很简单:
1.首先,设置初始化items为空数组[]
2.加入抓取方法,设置loading和错误信息
fetchData(url) { this.setState({ isLoading: true }); fetch(url) .then((response) => { if (!response.ok) { throw Error(response.statusText); } this.setState({ isLoading: false }); return response; }) .then((response) => response.json()) .then((items) => this.setState({ items })) // ES6 property value shorthand for { items: items } .catch(() => this.setState({ hasErrored: true })); }
|
这样,当组件安装时我们可以调用它:
componentDidMount() { this.fetchData('http://5826ed963900d612000138bd.mockapi.io/items'); }
|
这样,整个组件代码: class ItemList extends Component { constructor() { this.state = { items: [], }; } fetchData(url) { this.setState({ isLoading: true }); fetch(url) .then((response) => { if (!response.ok) { throw Error(response.statusText); } this.setState({ isLoading: false }); return response; }) .then((response) => response.json()) .then((items) => this.setState({ items })) .catch(() => this.setState({ hasErrored: true })); } componentDidMount() { this.fetchData('http://5826ed963900d612000138bd.mockapi.io/items'); } render() { } }
|
现在,我们已经有一个没有使用redux普通的抓取组件,从一个REST端点抓取条目列表,在条目全部抓取出现之前,显示loading。。状态,而如果发生错误,可以显示错误。
无论如何,一个组件不应该包含抓取数据的逻辑,数据也不应该保存在组件的状态中,要解决这两个问题,就需要redux。
下面我们将上面案例代码转换为redux,我们需要加入Redux,为了在Redux中实现异步调用,我们还需要Redux Thunk。
什么是Thunk?Thunk是一个函数,用于包装一个表达式以延迟其计算结果的获得,看下面代码:
// 立即计算 1 + 2 // x === 3 let x = 1 + 2;
// 1 + 2计算被延迟 // foo 能在以后被调用时再进行计算 // foo 就是一个 thunk! let foo = () => 1 + 2;
|
下面是在我们当前案例安装Redux, React Redux和Redux Thunk:
npm install redux react-redux redux-thunk --save
理解Redux
Redux有一些核心原理我们需要理解:
1.有一个全局状态对象管理整个应用的状态,在这个案例中,它是类似于我们这个最初组件的状态,这属于真相单一来源。
2.只有一个办法才能修改状态,只能通过一个动作action,这是一个用来表示改变操作的对象,Action Creators是专门用来派发每个改变的函数,所做的就是返回一个动作action对象。
3.当一个动作action被派发时,一个Reducer函数将实际改变这个动作所要改变的状态,如果动作不适合这个Reducer,就返回已经存在状态。
4.Reducer时纯函数,它们不会有副作用,没有可变状态,它们都必须返回一个修改的复制,
5.独立reducer被结合到单个rootReducer上,以创建状态的各种离散状态。
6.Store是将上面所有东西都结合一起,通过使用rootReducer代表状态,允许你真正dispatch动作action
7.对于在React中使用Redux,<Provider />组件包装整个应用,传递store下传到所有子成员。
设计状态
从开始工作到现在,我们已经明白我们状态需要有三个属性: items,hasErrored,isLoading,对应三个动作action。
但是Action Creators不同于动作action,不需要与状态有1:1关系,我们需要第四个action creator来调用其他三个action creators,这取决于抓取数据的状态,这第四个action creator几乎等同于我们这个案例原始fetchData()方法,但是不是使用this.setState({ isLoading: true }) 方法来直接修改状态,我们通过派发dispatch一个动作action来做同样事情:dispatch(isLoading(true))
创建动作action
让我们创建actions/items.js文件来放我们的action creators,我们首先开始3个简单动作:
export function itemsHasErrored(bool) { return { type: 'ITEMS_HAS_ERRORED', hasErrored: bool }; } export function itemsIsLoading(bool) { return { type: 'ITEMS_IS_LOADING', isLoading: bool }; } export function itemsFetchDataSuccess(items) { return { type: 'ITEMS_FETCH_DATA_SUCCESS', items }; }
|
正如之前提到,action creators会返回一个动作action,我们使用export以便以后在其它代码地方使用它们。第二个action create获取bool(true/false)作为参数,返回带有意义的一个type和一个分配有具体相应属性的bool这两个类型的对象。
第三个itemsFetchSuccess是当数据被成功抓取时调用,将数据作为items传递,通过ES6的value shorthands魔术,我们返回一个对象,带有属性items,它的值将是items的数组。
现在我们有3个动作action代表我们的状态,我们将转换原来组件的fetchData方法为itemsFetchData()这样新的action creator。
默认情况下,Redux的action creators不会支持异步动作,比如异步抓取数据,这里就需要使用Redux Thunk,它能让你编写action creator返回一个函数而不是一个动作action对象,内部函数能够接受store方法dispatch和getState作为参数,但是我们只使用dispatch。
如果不使用thunk,实现异步的最简单方法是5秒后手工触发itemsHasErrored方法:
export function errorAfterFiveSeconds() { // We return a function instead of an action object return (dispatch) => { setTimeout(() => { // This function is able to dispatch other action creators dispatch(itemsHasErrored(true)); }, 5000); }; }
|
现在有了Thunk,我们编写itemsFetchData方法:
export function itemsFetchData(url) { return (dispatch) => { dispatch(itemsIsLoading(true)); fetch(url) .then((response) => { if (!response.ok) { throw Error(response.statusText); } dispatch(itemsIsLoading(false)); return response; }) .then((response) => response.json()) .then((items) => dispatch(itemsFetchDataSuccess(items))) .catch(() => dispatch(itemsHasErrored(true))); }; }
|
待续。。。
A Dummy’s Guide to Redux and Thunk in React – Medi
[该贴被tecentID39C3D于2016-11-18 13:16修改过]