在这篇文章,我们将深入探讨 Redux 并了解为什么用它构建一个 React 应用时是很有价值的。我也将带您构建您的第一个 Redux 应用,包括如何使用 Stormpath React SDK 作为 身份认证 。之后您可以把这些知识应用到您现有的 React 应用中!
Redux 是一个帮您在应用中管理 state 的库。它的设计源自 Flux ,但是避开了 Flux 编写应用的复杂性。如果您已经编写了一个 Flux 应用,您会很快发现使用 Redux 需要手动编写之前所有的样板。此外与 Flux 不同的是,您有一个单一的 state 容器。这是一个很大的优势,因为它会使 state 共享和代码重用让您构建应用轻松很多。
store 仅是一个 state 容器。这是存储 state 并在哪里 actions 被调度及处理的地方。当您开始构建一个 Redux 应用,您要思考如何在应用中存储模块和 state。这很重要因为 Redux 建议只有一个 store,并且由于 state 共享这是之前想到的一个不错的想法。
Actions 是表示如何周转我们 state 状态的对象。您可以把 actions 视为 state 树的 API。为了说明,一个添加新用户的 action 可以是:
{ type: 'ADD_USER', data: { name: 'Foo', email: 'foo@bar.com', password: 'Foobar123_' } }
为了让操作变得更清晰和更容易复用,通常使用一个建造者模式来创建 action 对象。例如在上述情况,您可以为这个对象创建一个函数如 addUser(name, email, password)
。正如您所看到的,actions 本身并不操作任何东西。action 仅仅是一个描述我们如何改变 state 的对象。
Actions 很酷,但它们对自身并没有太大的意义。这就是 reducers 的由来。Reducers 在 store 中通过分发处理 action 来减少这些 actions 对 state 的改变。如果我们在 store 分发一个 action 如 ADD_USER
,我们可以用 reducer 将那些添加新用户的 action 入口到 state。
现在您了解了基础知识,让我们继续设计和构建第一个由 Redux 驱动的应用。
为了让操作变的简单,我们构建一个 to-do 应用。这样我们就可以玩转大部分 Redux 中最重要的概念而不是过于关注于应用自身。
如果我们想到一个 to-do 应用,我们将会需要一些基本的事项。首先一个 to-do 通常由一个列表组成。另外,这个列表包含我们可以更改的待办事项。
从一个 state 角度来看,我们这个应用的模型类似这样:
{ todo: { items: [ { message: "Finish Redux blog post...", completed: false } ] } }
添加待办事项到 state ?首先,我们希望添加新的待办事项到其中。让我们创建一个 action :
function addTodo(message) { return { type: 'ADD_TODO', message: message, completed: false }; }
注意这里的 type
字段。这个应该是个唯一的名称用于描述您的 action。通常这个类型是大写格式并用底划线作为单词分隔符。另外您将使用这个名称/标识符在 reducers 中处理具体的 actions 并改变它们的 state 。
一旦我们增加了新的待办事项,我们肯定希望能够将其标记为已完成,我们也希望能够将其删除,甚至可以清除所有待办事项。
因此让我们也为这些操作添加 actions :
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 处理。
import { createStore } from 'redux'; var defaultState = { todo: { items: [] } }; function todoApp(state, action) { } var store = redux.createStore(todoApp, defaultState);
现在当我们有一些 actions 和 store,让我们创建第一个 reducer。如果您还记的刚刚说的,reducer 只是一个处理器您可以用它来处理 actions 和改变 state。
因此我们开始处理 ADD_TODO
action 如下所示:
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 。
现在当我们有了第一个 action 处理器,让我们为它们增加其余的支持:
function todoApp(state, action) { switch (action.type) { case 'ADD_TODO': return Object.assign({}, 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({}, state, { todo: { items: items } }); case 'DELETE_TODO': var items = [].concat(state.todo.items); items.splice(action.index, 1); return Object.assign({}, state, { todo: { items: items } }); case 'CLEAR_TODO': return Object.assign({}, state, { todo: { items: [] } }); default: return state; } }
现在我们已经有了业务逻辑,让我们写一些 UI 代码。由于大部分是常见的 React 知识并且非常类似 Flux 构建的应用, 就不再深入讲解。
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 构建的应用。
因为有相当多需要增加 Redux 支持 Stormpath React SDK 需求,我们说干就干。如果您希望为 Redux 配置这个 SDK,简单的配置这个 Stormpath React SDK dispatcher
选项并设置 type
为 redux
指向 store
到您的 Redux store 如下所示:
function myApp(state, action) { return state; } ReactStormpath.init({ dispatcher: { type: 'redux', store: createStore(myApp) } });
一旦完成这些您可以拦截并处理一切 Stormpath React SDK 的 actions 调度。例如,您希望用用户数据来丰富 state,然后简单处理这个 USER_SET
action 并将用户数据添加到 state。
function myApp(state, action) { switch (action.type) { case 'USER_SET': return Object.assign({}, state, { user: action.data }); default: return state; } }
正如您在这篇文章所看到的,构建一个由 Redux 驱动的应用是非常容易和简单的。这很像构建一个 Flux, 只是是概念略有不同并有较少的样板代码编写。
希望您喜欢这个教程,能够在将来利用它。
如果遇到任何无法运行或者问题,不妨看下 参考应用 。
想了解更多关于 React 应用增加身份认证的内容?看看这些教程:
教程:构建一个 React.js 身份认证应用
自定义注册登录表单
原文地址: https://stormpath.com/blog/build-a-redux-powered-react-application/