在这篇文章,我们将深入探讨 Redux 并了解为什么用它构建一个 React 应用时是很有价值的。我也将带您构建您的第一个 Redux 应用,包括如何使用 Stormpath React SDK 作为 身份认证。之后您可以把这些知识应用到您现有的 React 应用中!@H_301_1@
什么是 Redux?
Redux 是一个帮您在应用中管理 state 的库。它的设计源自 Flux,但是避开了 Flux 编写应用的复杂性。如果您已经编写了一个 Flux 应用,您会很快发现使用 Redux 需要手动编写之前所有的样板。此外与 Flux 不同的是,您有一个单一的 state 容器。这是一个很大的优势,因为它会使 state 共享和代码重用让您构建应用轻松很多。@H_301_1@
Stores
store 仅是一个 state 容器。这是存储 state 并在哪里 actions 被调度及处理的地方。当您开始构建一个 Redux 应用,您要思考如何在应用中存储模块和 state。这很重要因为 Redux 建议只有一个 store,并且由于 state 共享这是之前想到的一个不错的想法。@H_301_1@
Actions
Actions 是表示如何周转我们 state 状态的对象。您可以把 actions 视为 state 树的 API。为了说明,一个添加新用户的 action 可以是:@H_301_1@
{ type: 'ADD_USER',data: { name: 'Foo',email: 'foo@bar.com',password: 'Foobar123_' } }
为了让操作变得更清晰和更容易复用,通常使用一个建造者模式来创建 action 对象。例如在上述情况,您可以为这个对象创建一个函数如 addUser(name,email,password)
。正如您所看到的,actions 本身并不操作任何东西。action 仅仅是一个描述我们如何改变 state 的对象。@H_301_1@
Reducers
Actions 很酷,但它们对自身并没有太大的意义。这就是 reducers 的由来。Reducers 在 store 中通过分发处理 action 来减少这些 actions 对 state 的改变。如果我们在 store 分发一个 action 如 ADD_USER
,我们可以用 reducer 将那些添加新用户的 action 入口到 state。@H_301_1@
构建 Redux 应用
现在您了解了基础知识,让我们继续设计和构建第一个由 Redux 驱动的应用。@H_301_1@
为了让操作变的简单,我们构建一个 to-do 应用。这样我们就可以玩转大部分 Redux 中最重要的概念而不是过于关注于应用自身。@H_301_1@
如果我们想到一个 to-do 应用,我们将会需要一些基本的事项。首先一个 to-do 通常由一个列表组成。另外,这个列表包含我们可以更改的待办事项。@H_301_1@
从一个 state 角度来看,我们这个应用的模型类似这样:@H_301_1@
{ todo: { items: [ { message: "Finish Redux blog post...",completed: false } ] } }
添加 Actions
添加待办事项到 state ?首先,我们希望添加新的待办事项到其中。让我们创建一个 action :@H_301_1@
function addTodo(message) { return { type: 'ADD_TODO',message: message,completed: false }; }
注意这里的 type
字段。这个应该是个唯一的名称用于描述您的 action。通常这个类型是大写格式并用底划线作为单词分隔符。另外您将使用这个名称/标识符在 reducers 中处理具体的 actions 并改变它们的 state 。@H_301_1@
一旦我们增加了新的待办事项,我们肯定希望能够将其标记为已完成,我们也希望能够将其删除,甚至可以清除所有待办事项。@H_301_1@
因此让我们也为这些操作添加 actions :@H_301_1@
function completeTodo(index) { return { type: 'COMPLETE_TODO',index: index }; } function deleteTodo(index) { return { type: 'DELETE_TODO',index: index }; } function clearTodo() { return { type: 'CLEAR_TODO' }; }
现在我们已经有了 actions,让我们继续构建 store。如果您还记的刚刚说的,store 是 Redux 应用的核心,关联所有的 state,调度 actions 和 reducers 处理。@H_301_1@
import { createStore } from 'redux'; var defaultState = { todo: { items: [] } }; function todoApp(state,action) { } var store = redux.createStore(todoApp,defaultState);
添加 Reducers
现在当我们有一些 actions 和 store,让我们创建第一个 reducer。如果您还记的刚刚说的,reducer 只是一个处理器您可以用它来处理 actions 和改变 state。@H_301_1@
因此我们开始处理 ADD_TODO
action 如下所示:@H_301_1@
function todoApp(state,action) { switch (action.type) { case 'ADD_TODO': return Object.assign({},state,{ items: items.concat([{ message: action.message,completed: false }]) }); default: return state; } }
注意当我们说一个 reducer “改变” state 时,如果一个 state 需要改变,实际它所做的是创建一个 state 副本并作出改变。如果没有变化,那么返回相同的 state。但在任何情况下您都不应该直接改变 state 因为这样意味着改变 state history 。@H_301_1@
现在当我们有了第一个 action 处理器,让我们为它们增加其余的支持:@H_301_1@
function todoApp(state,{ todo: { items: items.concat([{ message: action.message,completed: false }]) } }); case 'COMPLETE_TODO': var items = [].concat(state.todo.items); items[action.index].completed = true; return Object.assign({},{ todo: { items: items } }); case 'DELETE_TODO': var items = [].concat(state.todo.items); items.splice(action.index,1); return Object.assign({},{ todo: { items: items } }); case 'CLEAR_TODO': return Object.assign({},{ todo: { items: [] } }); default: return state; } }
把它们结合到一个 React 界面
现在我们已经有了业务逻辑,让我们写一些 UI 代码。由于大部分是常见的 React 知识并且非常类似 Flux 构建的应用,就不再深入讲解。@H_301_1@
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; var defaultState = { todo: { items: [] } }; // 添加我们在前面步骤中创建的 actions ... function todoApp(state,action) { // 添加我们在前面步骤中的 reducer 逻辑... } var store = redux.createStore(todoApp,defaultState); class AddTodoForm extends React.Component { state = { message: '' }; onFormSubmit(e) { e.preventDefault(); store.dispatch(addTodo(this.state.message)); this.setState({ message: '' }); } onMessageChanged(e) { var message = e.value.trim(); this.setState({ message: message }); } render() { return ( <form onSubmit={this.onFormSubmit.bind(this)}> <input type="text" placeholder="Todo..." onChange={this.onMessageChanged.bind(this)} value={this.state.message} /> <button type="submit" value="Add" /> </form> ); } } class TodoItem extends React.Component { onDeleteClick() { store.dispatch(deleteTodo(this.props.index)); } onCompletedClick() { store.dispatch(completeTodo(this.props.index)); } render() { return ( <li> <a href="#" onClick={this.onDeleteClick.bind(this)}>[x]</a> <a href="#" onClick={this.onCompletedClick.bind(this)}>{this.props.message}</a> </li> ); } } class TodoList extends React.Component { state = { items: [] }; componentWillMount() { store.subscribe(() => { var state = store.state(); this.setState({ items: state.todo.items }); }); } render() { var items = []; this.state.items.forEach((item,index) => { items.push(<TodoItem index={index} message={item.message} completed={item.completed} />); }); return ( <ol>{ items }</ol> ); } } ReactDOM.render( <div> <h1>Todo</h1> <AddTodoForm /><hr /> <TodoList /> </div>,document.getElementById('app') );
正如您所看到的,构建 Redux 应用 UI 部分并不难。唯一的区别就是您用 store.subscribe(listener)
来监听 state 改变,然后通过 store.getState()
检索 state。但除此之外,它非常像一个 Flux 构建的应用。@H_301_1@
Redux 支持 Stormpath React SDK
因为有相当多需要增加 Redux 支持 Stormpath React SDK 需求,我们说干就干。如果您希望为 Redux 配置这个 SDK,简单的配置这个 Stormpath React SDK dispatcher
选项并设置 type
为 redux
指向 store
到您的 Redux store 如下所示:@H_301_1@
function myApp(state,action) { return state; } ReactStormpath.init({ dispatcher: { type: 'redux',store: createStore(myApp) } });
一旦完成这些您可以拦截并处理一切 Stormpath React SDK 的 actions 调度。例如,您希望用用户数据来丰富 state,然后简单处理这个 USER_SET
action 并将用户数据添加到 state。@H_301_1@
function myApp(state,action) { switch (action.type) { case 'USER_SET': return Object.assign({},{ user: action.data }); default: return state; } }
结束语
正如您在这篇文章所看到的,构建一个由 Redux 驱动的应用是非常容易和简单的。这很像构建一个 Flux,只是是概念略有不同并有较少的样板代码编写。@H_301_1@
希望您喜欢这个教程,能够在将来利用它。@H_301_1@
如果遇到任何无法运行或者问题,不妨看下 参考应用。@H_301_1@
延伸阅读
想了解更多关于 React 应用增加身份认证的内容?看看这些教程:@H_301_1@
原文地址:https://stormpath.com/blog/build-a-redux-powered-react-application/@H_301_1@