教程:让咱们构建一个由 Redux 驱动的 React 应用

在这篇文章,咱们将深刻探讨 Redux 并了解为何用它构建一个 React 应用时是颇有价值的。我也将带您构建您的第一个 Redux 应用,包括如何使用 Stormpath React SDK 做为 身份认证。以后您能够把这些知识应用到您现有的 React 应用中!javascript

什么是 Redux?

Redux 是一个帮您在应用中管理 state 的库。它的设计源自 Flux,可是避开了 Flux 编写应用的复杂性。若是您已经编写了一个 Flux 应用,您会很快发现使用 Redux 须要手动编写以前全部的样板。此外与 Flux 不一样的是,您有一个单一的 state 容器。这是一个很大的优点,由于它会使 state 共享和代码重用让您构建应用轻松不少。html

Stores

store 仅是一个 state 容器。这是存储 state 并在哪里 actions 被调度及处理的地方。当您开始构建一个 Redux 应用,您要思考如何在应用中存储模块和 state。这很重要由于 Redux 建议只有一个 store,而且因为 state 共享这是以前想到的一个不错的想法。java

Actions

Actions 是表示如何周转咱们 state 状态的对象。您能够把 actions 视为 state 树的 API。为了说明,一个添加新用户的 action 能够是:react

{
  type: 'ADD_USER',
  data: {
    name: 'Foo',
    email: 'foo@bar.com',
    password: 'Foobar123_'
  }
}

为了让操做变得更清晰和更容易复用,一般使用一个建造者模式来建立 action 对象。例如在上述状况,您能够为这个对象建立一个函数如 addUser(name, email, password)。正如您所看到的,actions 自己并不操做任何东西。action 仅仅是一个描述咱们如何改变 state 的对象。git

Reducers

Actions 很酷,但它们对自身并无太大的意义。这就是 reducers 的由来。Reducers 在 store 中经过分发处理 action 来减小这些 actions 对 state 的改变。若是咱们在 store 分发一个 action 如 ADD_USER,咱们能够用 reducer 将那些添加新用户的 action 入口到 state。github

构建 Redux 应用

如今您了解了基础知识,让咱们继续设计和构建第一个由 Redux 驱动的应用。redux

为了让操做变的简单,咱们构建一个 to-do 应用。这样咱们就能够玩转大部分 Redux 中最重要的概念而不是过于关注于应用自身。app

若是咱们想到一个 to-do 应用,咱们将会须要一些基本的事项。首先一个 to-do 一般由一个列表组成。另外,这个列表包含咱们能够更改的待办事项。dom

从一个 state 角度来看,咱们这个应用的模型相似这样:函数

{
  todo: {
    items: [
      {
        message: "Finish Redux blog post...",
        completed: false
      }
    ]
  }
}

添加 Actions

添加待办事项到 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);

添加 Reducers

如今当咱们有一些 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;
  }
}

把它们结合到一个 React 界面

如今咱们已经有了业务逻辑,让咱们写一些 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 支持 Stormpath React SDK 需求,咱们说干就干。若是您但愿为 Redux 配置这个 SDK,简单的配置这个 Stormpath React SDK dispatcher 选项并设置 typeredux 指向 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 应用增长身份认证的内容?看看这些教程:

原文地址:https://stormpath.com/blog/build-a-redux-powered-react-application/

相关文章
相关标签/搜索