[译】Redux入门教程(二)

原Redux教程目录: javascript

上一篇文章更新了目录的1-10, 这篇教程更新11-17,看不懂的小伙伴必定要先去看第一篇

甩上一篇教程地址 【译】Redux入门教程(一)css

本次更新目录一览

重构reducer

在继续学习以前,来看看Redux的主要概念:html

  • Redux的store是大脑:它掌管Redux全部部分
  • 应用的状态经过store存在一个不可变的对象中
  • 一旦store收到了一个action, 它会触发一个reducer函数
  • 这个reducer返回下一个state

Redux的reducer是由什么组成的?vue

一个reducer是一个Javascript函数,它接收两个参数: state和action, 一个reducer函数可使用一个switch语句(可是我更倾向于使用if语句)来处理每一个ation类型java

reducer经过action的type来计算下一个state, 并且,当没有匹配到action的type的时候,它至少应该返回初始的statereact

当action的type匹配到一个有效的语句的时候,reducer就会计算下一个state而且返回一个新的对象npm

在以前的章节中,咱们建立了一个reducer,它除了返回初始的state什么也没作redux

打开src/js/reducers/index.js,而且更新以下:bootstrap

// src/js/reducers/index.js
import { ADD_ARTICLE } from "../constants/action-types";
const initialState = {
  articles: []
};
function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    state.articles.push(action.payload);
  }
  return state;
}
export default rootReducer;
复制代码

你发现了什么?api

尽管上面是有效的代码,可是他违背了Redux的原则:不变性(immutability)

数组的push方法不是一个纯函数:它改变了原数组,还有更严重的问题! 你记得Redux的第三个原则吗?state是不可变的而且任什么时候候都不能改变, 在咱们的reducer里面咱们改变了初始的对象!

咱们须要修复。首先咱们返回一个新的state,使用Object.assign获得的一个新的JavaScript对象, 经过这种方法,咱们保证了原始的state的不变性, 接下来咱们使用原生数组的conat方法代替push方法来保证原始数组不变,最终改造以下:

import { ADD_ARTICLE } from "../constants/action-types";
const initialState = {
  articles: []
};
function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    return Object.assign({}, state, {
      articles: state.articles.concat(action.payload)
    });
  }
  return state;
}
export default rootReducer;
复制代码

上面的例子保证了state是没有被修改的

初始的articels没有改变

初始的state对象也没有改变, 返回的state是初始state的一个拷贝

在Redux中避免变化有两个要点:

  • 对数组使用concat, slice和...展开运算
  • 对对象使用Objecy.assign和...展开运算

Redux技巧: 随着你的app愈来愈大,reducer也会愈来愈大,你能够将一个大的reducer函数分割成多个函数,而且使用combineReducers将他们结合起来

接下来,咱们将会在控制台玩Redux, 跟紧个人脚步!

Redux store的方法

我怕保证,下面的内容很快就能够学会

我想要你经过浏览器的控制台快速理解Redux是怎么工做的

Redux自己是一个小型库(2kKB), Redux的store暴露了简单的API来管理state, store最重要的方法是:

  • getState 获取应用的当前状态
  • dispatch 派发一个action
  • subscribe 监听state的变化

咱们将会在浏览器的控制台学习这些方法

为次,咱们必须将咱们以前建立的store和action暴露成全局变量

建立一个src/js/index.js文件,更新以下:

import store from "../js/store/index";
import { addArticle } from "../js/actions/index";
window.store = store;
window.addArticle = addArticle;
复制代码

如今打开src/index.js, 用下面的内容代替原先的内容:

import index from "./js/index"
复制代码

如今启动项目:

npm run start
复制代码

前往浏览器,打开http://localhost:8080/,而且按下F12打开控制台

由于咱们已经将store暴露为全局变量了,咱们能够获取到它的方法,试试吧!

开始获取当前的state:

store.getState()
复制代码

输出:

{articles: Array(0)}
复制代码

articles是空数组, 事实上,咱们还没更新初始state

subscribe方法接受一个回调函数,当action被派发的时候这个回调函数会被触发,派发一个action就是通知store咱们想要改变state

用下面的代码来注册回调函数:

store.subscribe(() => console.log('Look ma, Redux!!'))
复制代码

在Redux中要改变state,咱们须要派发action, 派发action须要调用dispath方法

咱们有一个action: addArticle-往state里面新增一项

用下面的代码派发action

store.dispatch(addArticle({ title: 'React Redux Tutorial for Beginners', id: 1 }) )
复制代码

当你运行上面的代码后,你就会看到

Look ma, Redux!!
复制代码

为了验证state真的发生了变化,再次运行store.getState() 输出{articles: Array(1)}

这就是Redux最简单的形式了

难吗?

一步步来探索这三个Redux方法,在控制台试验它们

  • getState 获取应用的当前状态
  • dispatch 派发一个action
  • subscribe 监听state的变化

这就是开始学习Redux你须要知道的全部东西

一旦你以为有信心继续学习下面的内容了,咱们将开始链接React和Redux!

链接React和Redux

学完Redux,我就意识到它并不复杂,我知道经过getState获取当前的state, 我知道怎么用dispatch派发一个action, 怎么用subscribe监听state的改变

而后我仍是不知道怎样将React和Redux结合在一块儿

我问我本身:我应该在一个React的组件中调用getState吗?我怎么在一个React组件中派发一个action?等等这些问题都困扰着我

Redux自己是一个独立的框架,你能够在普通的Javascript,或者框架如Angular,React中使用它,有种东西能够结合Redux和你最喜欢的框架。

对于React来讲就是react-redux

继续学习以前,先安装react-redux npm i react-redux --save-dev

为了演示React和Redux是怎么协同工做的,咱们来建一个超级简单的应用,这个应用由下面的组件组成:

  • 一个App组件
  • 一个展现全部文章的列表组件
  • 一个新增文章的表单组件

react-redux是什么

react-redux是一个将Redux绑定到React的轻巧库

接下来你将要认识一个很重要的方法connect

react-redux的connect到底作了什么?豪不惊讶,他将React的组件和Redux的store联系了起来

使用connect, 你能够传递两个或者三个参数,具体取决于用途,下面是须要知道的基本的东西

  • mapStateToProps函数
  • mapDispatchToProps函数

react-redux中mapStateToProps是干什么用的?mapStateToProps 的做用正如它的名字:它链接Redux state的一部分到React组件的props,这样,一个链接的React组件就可获取到它所须要的那部分store的数据

mapDispatchToProps又是干什么用的呢?它作了和mapStateToProps 类似的事,可是是针对actions的, ,这样,一个链接的React组件就能够派发actions了

如今一切是否是都清楚了?若是不清楚,停下来,从新读这份教程,我知道,有不少东西要学,要花不少时间,即便你不能立刻学会Redux也不要焦虑, 一切早晚都会清晰明了。

从下面开始,咱们要作点东西了!

App component 和Redux store

咱们已经知道,mapStateToProps链接Redux state的一部分到React组件的props中,你可能想知道,它能够链接整个Redux和React吗?是的,它不能 要想将Redux和React链接起来,咱们须要使用Provider

Provider是一个来自react-redux的高阶组件

用外行人的话来讲,Provider包裹你的React应用, 而且让它能够感知到整个Redux的store

为何要这样?咱们知道Redux的store管理一切,React必定要能够和store沟通才能够获取state和派发actions

打开src/js/index.js, 清除整个文件内容,替换成下面的内容(若是你是通react-create-app来搭建环境的话,你须要修改的是src/index.js这个文件)

import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import store from "./store/index";
import App from "./components/App.jsx";
// if you're in create-react-app import the files as:
// import store from "./js/store/index";
// import App from "./js/components/App.jsx";
render(
  <Provider store={store}> <App /> </Provider>,
  // The target element might be either root or app,
  // depending on your development environment
  // document.getElementById("app")
  document.getElementById("root")
);
复制代码

看到了吗?Provider 包裹了整个React应用,并且它获取store做为prop传递

如今咱们建立一个App组件,由于咱们立刻就须要它了,这个组件没什么特别:引入 List组件而且渲染本身

建立一个放组件的目录:

mkdir -p src/js/components
复制代码

在这目录里面增长一个App.jsx文件:

// src/js/components/App.jsx
import React from "react";
import List from "./List";
const App = () => (
  <div className="row mt-5"> <div className="col-md-4 offset-md-1"> <h2>Articles</h2> <List /> </div> </div>
);
export default App;
复制代码

保存文件,接下来建立List组件

List component 和 Redux state

目前为止,咱们作的都没什么特别的

可是咱们的新组件List将会和Redux的store交互

简要回顾:链接React组件和Redux的关键是connect

connect接收至少一个参数

由于咱们想要List组件获取文章列表,也就是要链接state.articles到这个组件中,怎么作呢?用mapStateToProps

在 src/js/components建立一个List.jsx文件,内容以下

import React from "react";
import { connect } from "react-redux";
const mapStateToProps = state => {
  return { articles: state.articles };
};
const ConnectedList = ({ articles }) => (
  <ul className="list-group list-group-flush"> {articles.map(el => ( <li className="list-group-item" key={el.id}> {el.title} </li> ))} </ul>
);
const List = connect(mapStateToProps)(ConnectedList);
export default List;
复制代码

List组件接收articels做为prop, articles是Redux的state的articels的一个拷贝, 它来自于reducer

const initialState = {
  articles: []
};
function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    return Object.assign({}, state, {
      articles: state.articles.concat(action.payload)
    });
  }
  return state;
}
复制代码

时刻牢记:redux的state来自于reducers, 如今须要利用JSX的prop来生成articles列表

{articles.map(el => (
  <li className="list-group-item" key={el.id}> {el.title} </li>
))}****
复制代码

React技巧:养成用PropTypes 验证props的习惯,或者使用TypeScript会更好

最后,组件将List导出, List是无状态组件ConnectedList和Redux store结合的结果

是否依然困惑?我也是,理解connect如何运做须要一些时间,不要怕, 通往Redux的学习之路充满了'啊?-啊!'的时刻

我建议你休息一下,再探索研究connect和mapStateToProps

一旦你对这些都成竹在胸了,你能够继续进行下面的学习了!

Form component 和 Redux actions

咱们即将建立的Form组件比List组件复杂一点,它是一个表单,能够添加新条目到应用中

除此以外,它是一个有状态的组件

一个有状态的组件是一个有本身的state的React组件

咱们如今在谈论用Redux管理状态,你为何要给Form组件自身状态呢?

即便使用Redux,咱们也能够有有状态的组件存在

并非每一个应用的状态都应该放在Redux中

在这个例子中,我不想要其余的组件知道这个Form组件本自身的state

form组件包含一些经过提交操做跟新本地状态的逻辑

它接收一个Redux的action,这样,它能够经过派发addArticle这个action更新全局的state

在src/js/components新增Form.jsx , 内容以下

// src/js/components/Form.jsx
import React, { Component } from "react";
import { connect } from "react-redux";
import uuidv1 from "uuid";
import { addArticle } from "../actions/index";
function mapDispatchToProps(dispatch) {
  return {
    addArticle: article => dispatch(addArticle(article))
  };
}
class ConnectedForm extends Component {
  constructor() {
    super();
    this.state = {
      title: ""
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({ [event.target.id]: event.target.value });
  }
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id });
    this.setState({ title: "" });
  }
  render() {
    const { title } = this.state;
    return (
      <form onSubmit={this.handleSubmit}> <div className="form-group"> <label htmlFor="title">Title</label> <input type="text" className="form-control" id="title" value={title} onChange={this.handleChange} /> </div> <button type="submit" className="btn btn-success btn-lg"> SAVE </button> </form> ); } } const Form = connect(null, mapDispatchToProps)(ConnectedForm); export default Form; 复制代码

除了mapDispatchToProps和connect,这个组件是标准的React组件

mapDispatchToProps链接Redux的actions到React组件的props,这样,一个链接的组件就能够派发actions了

经过handleSubmit方法你能够明白action是如何被派发的

// ...
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id }); // Relevant Redux part!!
// ...
  }
// ...
复制代码

最后组件导出为Form,它是ConnectedForm和Redux store结合后的结果

注意:当mapStateToProps 不存在的时候,connect的第一个参数必须是null,不然你会获得TypeError:dispatch 不是一个函数

咱们的组件都设置好了!

更新App组件,引入Form 组件

import React from "react";
import List from "./List.jsx";
import Form from "./Form.jsx";
const App = () => (
  <div className="row mt-5">
    <div className="col-md-4 offset-md-1">
      <h2>Articles</h2>
      <List />
    </div>
    <div className="col-md-4 offset-md-1">
      <h2>Add a new article</h2>
      <Form />
    </div>
  </div>
);
export default App;
复制代码

安装uuid

npm i uuid --save-dev
复制代码

运行应用

npm run start
复制代码

前往浏览器打开http://localhost:8080,你会看到以下的页面

页面没有任何奇特的东西,可是它显示了React和Redux正在工做!

左边的List组件链接着Redux的store,当你添加一个条目的时候,它会从新渲染

若是你在页面上什么都没看到,确保你在 src/js/index.js中写了 document.getElementById(“app”) ****来匹配一个页面中真实存在的元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" >
    <title>How to set up React, Webpack, and Babel</title>
</head>
<body>
    <div class="container">
        <div id="root">
        </div>
    </div>
</body>
</html>
复制代码

别忘记引入Bootstrap ,可是咱们还没作完,接下来,咱们来看看Redux的中间件,坚持住!


本次更新完毕,有错误和翻译不许确的地方,欢迎你们指出,一块儿学习进步!!!

近期文章:

相关文章
相关标签/搜索