分类

Redux 入门教程(一):基本用法

作者: 阮一峰

日期: 2016年9月18日

一年半前,我写了《React 入门实例教程》,介绍了 React 的基本用法。

React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。

  • 代码结构
  • 组件之间的通信

对于大型的复杂应用来说,这两方面恰恰是最关键的。因此,只用 React 没法写大型应用。

为了解决这个问题,2014年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。

本文详细介绍 Redux 架构,由于内容较多,全文分成三个部分。今天是第一部分,介绍基本概念和用法。

零、你可能不需要 Redux

首先明确一点,Redux 是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React 就够了。

曾经有人说过这样一句话。

"如果你不知道是否需要 Redux,那就是不需要它。"

Redux 的创造者 Dan Abramov 又补充了一句。

"只有遇到 React 实在解决不了的问题,你才需要 Redux 。"

简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。

  • 用户的使用方式非常简单
  • 用户之间没有协作
  • 不需要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

上面这些情况,都不需要使用 Redux。

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

上面这些情况才是 Redux 的适用场景:多交互、多数据源。

从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

发生上面情况时,如果不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快就会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改变状态、传播状态的变化。

总之,不要把 Redux 当作万灵丹,如果你的应用没那么复杂,就没必要用它。另一方面,Redux 只是 Web 架构的一种解决方案,也可以选择其他方案。

一、预备知识

阅读本文,你只需要懂 React。如果还懂 Flux,就更好了,会比较容易理解一些概念,但不是必需的。

Redux 有很好的文档,还有配套的小视频(前30集后30集)。你可以先阅读本文,再去官方材料详细研究。

我的目标是,提供一个简洁易懂的、全面的入门级参考文档。

二、设计思想

Redux 的设计思想很简单,就两句话。

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

请务必记住这两句话,下面就是详细解释。

三、基本概念和 API

3.1 Store

Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。

Redux 提供createStore这个函数,用来生成 Store。


import { createStore } from 'redux';
const store = createStore(fn);

上面代码中,createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。

3.2 State

Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。

当前时刻的 State,可以通过store.getState()拿到。


import { createStore } from 'redux';
const store = createStore(fn);

const state = store.getState();

Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。

3.3 Action

State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。

Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置,社区有一个规范可以参考。


const action = {
  type: 'ADD_TODO',
  payload: 'Learn Redux'
};

上面代码中,Action 的名称是ADD_TODO,它携带的信息是字符串Learn Redux

可以这样理解,Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store。

3.4 Action Creator

View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。


const ADD_TODO = '添加 TODO';

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

const action = addTodo('Learn Redux');

上面代码中,addTodo函数就是一个 Action Creator。

3.5 store.dispatch()

store.dispatch()是 View 发出 Action 的唯一方法。


import { createStore } from 'redux';
const store = createStore(fn);

store.dispatch({
  type: 'ADD_TODO',
  payload: 'Learn Redux'
});

上面代码中,store.dispatch接受一个 Action 对象作为参数,将它发送出去。

结合 Action Creator,这段代码可以改写如下。


store.dispatch(addTodo('Learn Redux'));

3.6 Reducer

Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。

Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。


const reducer = function (state, action) {
  // ...
  return new_state;
};

整个应用的初始状态,可以作为 State 的默认值。下面是一个实际的例子。


const defaultState = 0;
const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};

const state = reducer(1, {
  type: 'ADD',
  payload: 2
});

上面代码中,reducer函数收到名为ADD的 Action 以后,就返回一个新的 State,作为加法的计算结果。其他运算的逻辑(比如减法),也可以根据 Action 的不同来实现。

实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。


import { createStore } from 'redux';
const store = createStore(reducer);

上面代码中,createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。

为什么这个函数叫做 Reducer 呢?因为它可以作为数组的reduce方法的参数。请看下面的例子,一系列 Action 对象按照顺序作为一个数组。


const actions = [
  { type: 'ADD', payload: 0 },
  { type: 'ADD', payload: 1 },
  { type: 'ADD', payload: 2 }
];

const total = actions.reduce(reducer, 0); // 3

上面代码中,数组actions表示依次有三个 Action,分别是加0、加1和加2。数组的reduce方法接受 Reducer 函数作为参数,就可以直接得到最终的状态3

3.7 纯函数

Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。

纯函数是函数式编程的概念,必须遵守以下一些约束。

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。


// State 是一个对象
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // 或者
  return { ...state, ...newState };
}

// State 是一个数组
function reducer(state, action) {
  return [...state, newItem];
}

最好把 State 对象设成只读。你没法改变它,要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变的对象。

3.8 store.subscribe()

Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。


import { createStore } from 'redux';
const store = createStore(reducer);

store.subscribe(listener);

显然,只要把 View 的更新函数(对于 React 项目,就是组件的render方法或setState方法)放入listen,就会实现 View 的自动渲染。

store.subscribe方法返回一个函数,调用这个函数就可以解除监听。


let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe();

四、Store 的实现

上一节介绍了 Redux 涉及的基本概念,可以发现 Store 提供了三个方法。

  • store.getState()
  • store.dispatch()
  • store.subscribe()

import { createStore } from 'redux';
let { subscribe, dispatch, getState } = createStore(reducer);

createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。


let store = createStore(todoApp, window.STATE_FROM_SERVER)

上面代码中,window.STATE_FROM_SERVER就是整个应用的状态初始值。注意,如果提供了这个参数,它会覆盖 Reducer 函数的默认初始值。

下面是createStore方法的一个简单实现,可以了解一下 Store 是怎么生成的。


const createStore = (reducer) => {
  let state;
  let listeners = [];

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };

  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener);
    }
  };

  dispatch({});

  return { getState, dispatch, subscribe };
};

五、Reducer 的拆分

Reducer 函数负责生成 State。由于整个应用只有一个 State 对象,包含所有数据,对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大。

请看下面的例子。


const chatReducer = (state = defaultState, action = {}) => {
  const { type, payload } = action;
  switch (type) {
    case ADD_CHAT:
      return Object.assign({}, state, {
        chatLog: state.chatLog.concat(payload)
      });
    case CHANGE_STATUS:
      return Object.assign({}, state, {
        statusMessage: payload
      });
    case CHANGE_USERNAME:
      return Object.assign({}, state, {
        userName: payload
      });
    default: return state;
  }
};

上面代码中,三种 Action 分别改变 State 的三个属性。

  • ADD_CHAT:chatLog属性
  • CHANGE_STATUS:statusMessage属性
  • CHANGE_USERNAME:userName属性

这三个属性之间没有联系,这提示我们可以把 Reducer 函数拆分。不同的函数负责处理不同属性,最终把它们合并成一个大的 Reducer 即可。


const chatReducer = (state = defaultState, action = {}) => {
  return {
    chatLog: chatLog(state.chatLog, action),
    statusMessage: statusMessage(state.statusMessage, action),
    userName: userName(state.userName, action)
  }
};

上面代码中,Reducer 函数被拆成了三个小函数,每一个负责生成对应的属性。

这样一拆,Reducer 就易读易写多了。而且,这种拆分与 React 应用的结构相吻合:一个 React 根组件由很多子组件构成。这就是说,子组件与子 Reducer 完全可以对应。

Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。


import { combineReducers } from 'redux';

const chatReducer = combineReducers({
  chatLog,
  statusMessage,
  userName
})

export default todoApp;

上面的代码通过combineReducers方法将三个子 Reducer 合并成一个大的函数。

这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。如果不同名,就要采用下面的写法。


const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
})

// 等同于
function reducer(state = {}, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  }
}

总之,combineReducers()做的就是产生一个整体的 Reducer 函数。该函数根据 State 的 key 去执行相应的子 Reducer,并将返回结果合并成一个大的 State 对象。

下面是combineReducer的简单实现。


const combineReducers = reducers => {
  return (state = {}, action) => {
    return Object.keys(reducers).reduce(
      (nextState, key) => {
        nextState[key] = reducers[key](state[key], action);
        return nextState;
      },
      {} 
    );
  };
};

你可以把所有子 Reducer 放在一个文件里面,然后统一引入。


import { combineReducers } from 'redux'
import * as reducers from './reducers'

const reducer = combineReducers(reducers)

六、工作流程

本节对 Redux 的工作流程,做一个梳理。

首先,用户发出 Action。


store.dispatch(action);

然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。


let nextState = todoApp(previousState, action);

State 一旦有变化,Store 就会调用监听函数。


// 设置监听函数
store.subscribe(listener);

listener可以通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。


function listerner() {
  let newState = store.getState();
  component.setState(newState);   
}

七、实例:计数器

下面我们来看一个最简单的实例。


const Counter = ({ value }) => (
  <h1>{value}</h1>
);

const render = () => {
  ReactDOM.render(
    <Counter value={store.getState()}/>,
    document.getElementById('root')
  );
};

store.subscribe(render);
render();

上面是一个简单的计数器,唯一的作用就是把参数value的值,显示在网页上。Store 的监听函数设置为render,每次 State 的变化都会导致网页重新渲染。

下面加入一点变化,为Counter添加递增和递减的 Action。


const Counter = ({ value, onIncrement, onDecrement }) => (
  <div>
  <h1>{value}</h1>
  <button onClick={onIncrement}>+</button>
  <button onClick={onDecrement}>-</button>
  </div>
);

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

const store = createStore(reducer);

const render = () => {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => store.dispatch({type: 'INCREMENT'})}
      onDecrement={() => store.dispatch({type: 'DECREMENT'})}
    />,
    document.getElementById('root')
  );
};

render();
store.subscribe(render);

完整的代码请看这里

Redux 的基本用法就介绍到这里,下一次介绍它的高级用法:中间件和异步操作。

(完)

留言(133条)

redux这一块难咬的骨头,期待阮老师的redux解读,占楼。

初学者,期待后续教程

Redux 的工作流程图中指向Action Creators的箭头画偏了

期待阮老师后续教程

写的非常好,不知阮老师什么时候更新后续

先顶再看,阮哥出品必属精品

一直都看阮老师的博客,写得太好了。通俗易懂,简单明了,需要强大的功底。期待老师的续讲!

个人觉得redux唯一的好处就是增加了一点代码的可维护性, 依靠强制reducer保持简单, component仅仅处理一些UI logic, 使用provider 和 connect来向component中注入state, 但这些并不会减少代码量, 而且作者特意强调的container component和presentation component, 这种区分当然是好的, 但并没有带来复用的好处,就像作者自己做的TODOMVC,里面无论是哪个component都没有复用性可言,是真正的demo oriented programming

希望后面几期能捎带手讲讲 redux-saga

reducer拆封没看懂

不错,我昨天还在想,你什么时候出这个rudux的东西。

這部分還是推薦看Dan的視頻,跟他走一遍TodoList的流程,就會了解Redux解決了哪些問題。

react的一些概念还是没有吃透~~~~为自己加油,感谢阮一峰老师

redux本身很简单,麻烦的是后面用到的异步action,然后各种插件,特别是需要结合react时,redux-thunk,redux-form,react-router-redux各种插件结合用。

拿个项目练练手就好,关键在于state结构的设计,不然项目膨胀后,自己都不知道去哪找需要的值。

工具redux devtools必备。

然后就是各种辅助了,webpack打包,测试。

继续关注博主的这个系列

引用fusun的发言:

reducer拆封没看懂

其实这玩意就是一个强制约束,reducer 用一行代码解释就是

 (state, action) => state

返回一个新的state, 将原有的值覆盖,记住3大原则就好。然后习惯了,就会觉得很妙了。

Reducer 函数拆分这里

const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action),
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};

原来是根据action.type进行判断,到了这里就看不懂了,求详解

最后那个计数,实在不能苟同,这么写,React就不是React了,忽略了React状态机的本质

const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action),
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};
//为什么这里返回的一个对象?怎生产生新的state呢?

在Redux之前,是否应该对flux做个介绍?
求《flux入门教程》。

3.4里面的对象好像落了key

@yy:这是ES6的语法,请看下面的链接

http://es6.ruanyifeng.com/#docs/object#属性的简洁表示法

请教,使用了websocket,为什么就需要用redux了,搜索了下没找到相关文章

给个推荐看的链接也行

虽然用redux已经有一段时间了,但是只能说会用,没有捋清楚底层的用意,老师的讲解从更高的层次进行了讲解,有种豁然开朗的感觉,感谢

引用geeksuen的发言:

Reducer 函数拆分这里

const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action),
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};

原来是根据action.type进行判断,到了这里就看不懂了,求详解

我也没看懂。。。

有没有react-redux的大神 妹子我有问题想请教 thank u

中等规模的项目就可以用,刚用react的时候的第一个项目没用,后来就发现数据与ui层的状态互相交织挺乱的
第二个项目用redux很好的分离了,不过确实是需要多写两层action,和reducer,好在增加了这两个货之后测试真的变得方便了很多,以前好多很难测的场景都被redux分离了出来.

看到 React-Router 后一直期待 React-Redux 系列。。。

感觉iFlex2要比redux好用
https://github.com/QianmiOpen/iflux2

有个问题 State 对象设成只读,这个该如何设置?

store.subscribe(listener);
显然,只要把 View 的更新函数(对于 React 项目,就是组件的render方法或setState方法)放入listen,就会实现 View 的自动渲染。

listener是组件的render方法好理解,但是setState方法作为listener,表示很费解,不是说store的state是映射到组件的props上的么?不是跟组件的state没关系么?

求解

关于createStore中subscribe的实现:
没理解为什么最后要返回
return () => {
listeners = listeners.filter(l => l !== listener);
}
感觉没什么用

@geeksuen:

const combineReducers = reducers => {
return (state = {}, action) => {
return Object.keys(reducers).reduce(
(nextState, key) => {
nextState[key] = reducers[key](state[key], action);
return nextState;
},
{}
);
};
};

还是根据action.type进行判断的,当store.dispatch(action)时,会执行所有的reducer,如果action.type在reducer里面做switch操作的时候匹配不到,是不会改变state的。

比如你combineReducers({key1:reducer1,key2:reducer2}),当store.dispatch(action)时,reducer1,reducer2都会执行。
关键时看combineReducers执行完后,得到的合并的reducer函数

关键在这里reducerKeys.reduce(function(nextState,reducerKey){...},{})理解了这一段,就会觉得豁然开朗。

再次感谢阮老师,分享那么好的文章~~

引用刀尖的发言:

最后那个计数,实在不能苟同,这么写,React就不是React了,忽略了React状态机的本质

我也感觉是,计数的demo感觉靠的是刷新整个Component,忽略了React本身的状态机制。这和用jQuery重新刷新整个页面本质上没什么区别。

3.6 Reducer

const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];

const total = actions.reduce(reducer, 0); // 3

前几天自己看资料写了一个demo.其他的都能理解,这一块实在没看懂. 有人懂的人把代码补全么?

@赵春森:

已经找到了,和redux无关. 自己知识量不够. 参考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

阮老师:请问react如何解决移动端click事件300ms左右延迟的问题

大神,我微博居然一直在关注你,刚刚看到你的个人网站,教程很有用~~~

引用kanglin的发言:

const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action),
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};
//为什么这里返回的一个对象?怎生产生新的state呢?

返回的是一整个state对象,然后根据需要取state对象里面的具体属性

MultCloud(https://www.multcloud.com/), a FREE and easy-to-use web app, supports for Managing Files and Transferring Files across Cloud Drives. Free provide 10TB traffic for data transmission.

引用jaychang的发言:

关于createStore中subscribe的实现:
没理解为什么最后要返回
return () => {
listeners = listeners.filter(l => l !== listener);
}
感觉没什么用

这个很简单,直接使用subscribe会把你要监听的事件添加到数组,但是这个方法本身返回值是也是一个方法(简称unSubscribe方法),执行这个unSubscribe会移除监听事件,所以作者在写这个方法时候通过filter过滤,执行的时候将之前本身已经添加进数组的事件名移除数组。

其实就是Erlang的思想

很好的文章,这些目前都用上了

用上了,谢谢!看一遍就有很好的了解!教程行云流水!

Redux 的设计思想很简单,就两句话。
(1)Web 应用是一个状态机,视图与状态是一一对应的。
(2)所有的状态,保存在一个对象里面。

这里面的“所有的状态,保存在一个对象里面。”,也包括components 的state吗?

const Counter = ({ value }) => (
{value}
+
-
);
阮老师,这个地方没有看懂
1.value通过解构赋值,但是onIncrement和onDecrement怎么取到值?
2.三个元素不是要放在一个div里面吗

state不是应该对应每个view吗?全局只有一个store,但是可以有多个state吗?您在第五部分说全局只有一个state?

ha,看了阮老师的教程,终于把redux搞懂了

最后一个例子,Counter组件中,传入的参数不应该是{value} ,应该是{value,onIncrement,onDecrement}吧,不然,经实测,函数总报错onIncrement和onDecrement未定义。
还有一个问题,这里之所以传入一个对象,是理解为对象的解构赋值吗,这个对象与Counter组件的属性对象相对应?

我刚才留言成功了怎么没有显示?再来一发。
@软刀

combineReducers 没看懂的同学看这里,官网文档:
https://github.com/reactjs/redux/blob/master/docs/api/combineReducers.md

const rootReducer = combineReducers({
numReducer, //相当于一个同名的 key:value, 通过 store.getState().numReducer 获取对应的state
});

这样是不是懂了呢~~

疑问:

1. 调用 store.dispatch(action) 的时候,每个reducer都会收到然后在switch,会不会有性能问题?能不能直接发给对应的reducer?

2.获取数据的时候store.getState()是获取全部数据, 虽然可以store.getState().xxxRender 获取对应render的state, 但每次都要获取所有数据会不会有性能问题?

function listerner() {
let newState = store.getState();
component.setState(newState);
}

为什么需要手动修改state了 , state不是已经改变状态了吗?自动会更新对应的view吧

看了代码分层与业务的分分合合,只能说js越来越趋同java了。

这个redux有没有中文的教程

给网站整个返回顶部的功能啊

View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。

const ADD_TODO = '添加 TODO';

function addTodo(text) {
return {
type: ADD_TODO,
text /// --------------> 这里少了key 吧 ??? payload : text
}
}

const action = addTodo('Learn Redux');


引用子龙的发言:

给网站整个返回顶部的功能啊

你把井号后面的字符删掉不就上去了啊

引用思密达的发言:

你把井号后面的字符删掉不就上去了啊

一口老血

const Counter = ({ value }) => (
{value}
+
-
);
阮老师,这是怎么回事,这是一个 箭头函数 返回HTML片段?

const render = () => {
ReactDOM.render(
value={store.getState()}
onIncrement={() => store.dispatch({type: 'INCREMENT'})}
onDecrement={() => store.dispatch({type: 'DECREMENT'})}
/>,
document.getElementById('root')
);
};

这里面的 Counter 又是一个 什么东西?

阮老师好,我查看了很多地方都没发现es6里的 ...三个点的写法您有时间时帮忙补充一下这方面的知识感谢,再次从您的播客中又得到知识财富

... state --是什么原理啊,我google不出来

const userReducer =(state={},action) => {

state = {... state,name:action.payload}

return state
}

引用刀尖的发言:

最后那个计数,实在不能苟同,这么写,React就不是React了,忽略了React状态机的本质

暂一个,确实如此

j为什么非要用es6的写法来写呢,看的人感觉很高大上,但是你忽略了大伙儿的层次,好吧,只有我最差么

引用李建文的发言:

最后一个例子,Counter组件中,传入的参数不应该是{value} ,应该是{value,onIncrement,onDecrement}吧,不然,经实测,函数总报错onIncrement和onDecrement未定义。
还有一个问题,这里之所以传入一个对象,是理解为对象的解构赋值吗,这个对象与Counter组件的属性对象相对应?

这是一个ES6语法!!!!


import { combineReducers } from 'redux';

const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
})

export default todoApp;

最后一行有笔误,export的不是todoApp,而应该是chatReducer

很好,很受益,一直在看阮大神的教程

createStore简单实现,道破天机阿

我整理看了下,感觉就是一个基于订阅发布模式,这种模式,如果在每个组件中都存在大量的dispatch,对后续维护,个人觉得很难

引用软刀的发言:

我也没看懂。。。

这个是对象的解构,因为返回的state有三个对应的属性名

阮老师,Action Creator这个函数,使用的场景应该是当有很多个view去触发同一种action,而不是你说的有多少种view就有多少种action,这个函数的action其实是同一种。

老师,你的最后一个计时器例子运行不了,一个是Counter组件没有主标签,会报一个组件只能有一个主标签的错误,另外Counter组件中会报找不到onIncrement,onDecrement这两个属性,应该定义的是Counter组件的属性参数应该是{value,onIncrement,onDecrement},写法:
const Counter = ({
value,onIncrement,onDecrement
}) => (

{value}
+
-

);

这才是如雷贯耳,网上的文章都是shit,要想明白react-native到底怎么写,必须得看阮一峰的文章,别人的文章我看不进去

整条技术栈都快跟阮老师学完了........阮老师的BLOG每条都有用.

阮老师,你好,现在redux的作者都推荐了mobx,mobx相对来讲更容易上手,直观,问下是否要转到mobx还是redux使用更加便捷

引用atom的发言:

有一个 Redux 莞式教程 https://github.com/kenberkeley/redux-simple-tutorial,貌似跟博主的行文差不多。。。

的确非常相似,但看了一下貌似是github上这个先出

我的哥哥,这都一年之前的文章了,我看官网redux的30个视频,愣是看到一半无法看下去看不懂,看了gitbook文章照着敲也没弄好,想找更加简单的例子便于理解,阮老师的文章对我有帮助,谢谢谢

我想知道 formValues 这个方法是如何用的

看3遍了,终于看懂了

看了两天好几个教程,头都大了,困惑在基本问题上,
组件怎么绑定的action?
reducer怎么派发新的state给组件?
就是这家问题搞的十分上火,教程常常有"专家盲点",又看了一晚上才算搞清楚,我大概总结下,或许有误,还望指证:
在组件上onclick=function(){store.dispatch({type:"动作A”,其他的数据})}就是绑定一个action;
触发后会就会调用reducer,reducer会反回一个state值;
新的值会依据store.subscribe(希望被更的新组件)传到组件"家门口",同时在组件里用store.getState()方法读取【此刻别的组件不能访问这个值么?】

之前一直是用redux/reflux管理全局和需要共享的状态,组件内部不需要分享的状态组件自己管理,到新公司后发现,项目用的reflux,每个组件都有一个store,每个组件内部的状态变化都要用reflux的action触发,前端老大说redux/reflux的单向数据流就是这样。我就很困惑了,组件内不被分享的状态变化直接用setState就够了吧?既然不被分享,这些状态的变化在组件内不都是可追踪的么,一想到要用redux重写项目我就觉得头疼,每个状态变化都要写action、dispatch、reducer。。。

引用echoloyuk的发言:

我也感觉是,计数的demo感觉靠的是刷新整个Component,忽略了React本身的状态机制。这和用jQuery重新刷新整个页面本质上没什么区别。

首先这个例子是redux官方例子,redux是全局状态管理,本身就是要尽量去除react的状态管理机制,实现单向数据流。其次这个例子和用jQuery重新刷新整个页面本质上确实没什么区别,因为这里主要是讲redux的,主要讲如何管理状态,而不是如何渲染dom,如果想看react和redux集成,请看react-redux,阮老师后面的教程有

说再多还不如多来点实际的demo配合一起讲解,这样明白的更快些

谢谢,讲的很透彻

有点不懂!

这里描述不清晰,建议去:http://redux.js.org/docs/basics/Reducers.html

看了两遍,总算看明白了..

const Counter = ({ value }) => (
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
);

阮老师,关于这一部分,我有两个疑问:

一、()内的标签是不是只能有一个外层标签?

二、这里的参数onIncrement和onDecrement是不是没有引入?

我自己在运行上面的代码时出现了这两个问题。

@zj:

谢谢指出,原代码确实有你说的问题,我已经改过来了。

老师 网站背景色可以换一下吗? 或者把那个暗黄色去掉吧,效果要很好,我有强迫症,每次看着都很难受。 O(∩_∩)O

/* background-color: #f5f5d5; */

入门的时候,看了好几遍您的redux系列文章,现在感觉也做过些demo及项目,打算重新系统学一遍redux基础,其中可能会引用您的博客内容~

“五、Reducer的拆分
Reducer 函数负责生成 State。由于整个应用只有一个 State 对象,包含所有数据,对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大。”
这里应该是整个应用只有一个Store对象吧,后面的State也应该是Store吧

看了createStore的源码,在看简单实现,不能佩服更多,赞!!!

计数器的那个案例下载下来如何跑起来啊?

刚开始学react,对redux不了解,不知道什么时候用,多学学教程,我想,应该就能懂,谢谢阮老师。

开题说了react有两个问题没涉及到 其中有一个代码结构,这是为什么,不是都已经做成组件化了吗,这样不算是代码结构吗

大概是去年(2016)年冬天看到这篇文章,当时就Mark了,一直没下决心仔细研读;
今年(2017)又到冬天了,躲在被窝里读完后仍然很是受用,下一步继续跟进学习dva,
多谢分享知识,知识分享!

引用keminu的发言:

state不是应该对应每个view吗?全局只有一个store,但是可以有多个state吗?您在第五部分说全局只有一个state?

我也觉得奇怪。。

> Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。

这句话不对。subscribe方法并没有监听什么。他唯一的作用就是把listener也就是render方法放入到listener的list中去。

每当dispatch 一个 action 的时候,dispatch方法必定会调用render了。

我的计数器无效,store.getState得到的值是改变了,但是页面没有改变,我最后通过this.setState把新值set到this.state中,然后页面中展示的是this.state.value

这样搞好奇怪,待我仔细看看哪里出问题了。

手动点赞,看了redux的三篇文章,对redux有了初步的认识,接下来就是在项目中使用巩固深入了,谢谢~

我看了下坛子里的留言,根据这些留言我建议你别在项目里使用 Redux, 老板会疯掉的

第一次看Redux,看了一遍没有看懂,准备再研究下,大神们有什么好的快速的方法吗?

这篇文章太好了

引用刀尖的发言:

最后那个计数,实在不能苟同,这么写,React就不是React了,忽略了React状态机的本质

我也是说,忽略了react自己的状态管理呀。 这个对新手不友好。

楼主的排版布局很清晰,赞!

受教,讲的真的很清晰

这篇博文写的不太好,没有Redux官方教程写的清晰。此文夸大了Redux的难度,Redux是一个非常简单清晰的设计。

再补一下,Redux的设计理念,跟帧同步是一样的。

知识点很多。但是如果我们尽量不用redux,那什么时候才能会呢?应该尽量使用

引用刘迪的发言:

阮老师:请问react如何解决移动端click事件300ms左右延迟的问题

这种问题大神又怎么可能回答,人家不屑啊,所以我就不问

如果reducer修改state中的一个数组中的一个元素,那还算纯函数吗,如果不算,应该如何修改state中的数组中的元素呢,希望大佬解答一下。

老大什么时候讲下DVA?

在下没什么文化,只能用这句话表达对阮老师的看法,
阮老师牛 逼

每次阅读都有新的收获,非常棒!

看了后,感觉瞬间通透,厉害了,阮老师

最近想学redux,不知道看这篇文章会不会过时

引用cx的发言:

最近想学redux,不知道看这篇文章会不会过时

还可以吧

我看了这个讲个真是没明白,一般我没看明白的,说明讲的就是不好。不清晰,让我看不到真正的redux原理。看完了依旧没看出什么东西。

踩坑后看到,被感动到,谢谢

看了官方文档,再看这篇文章感觉理清楚了很多

引用jaychang的发言:

有个问题 State 对象设成只读,这个该如何设置?

Ojbect.defineProperty

感觉有点零碎,看完后需要再整理一下思路

这种教程才叫做教程,看到的其他很多教程叫做手册才对。教程最忌讳的就是每一节内容引用大量延伸阅读、引用后面章节的链接。这让学习者不能聚焦当前的内容,而又害怕自己不点链接会错过什么。
像博主精心整理的教程,为学习者由浅入深的介绍,谢绝大量延伸的内容。因为只有简单的入门了,学习了运行机制,应用一段时间后,再去了解延伸的生态也不迟。

引用李红的发言:

如果reducer修改state中的一个数组中的一个元素,那还算纯函数吗,如果不算,应该如何修改state中的数组中的元素呢,希望大佬解答一下。

用数组的map方法,把数组每一项和修改后的值做比较,如果是被修改过的值,返回修改后的,其他的原样返回

基础不够没看懂

阮老师的这篇文章我要是早点看到,就不会学redux那么痛苦了,大师果然是大师,语言精练,通俗易懂,非常感谢

这篇教程比官方教程强多了,写地非常容易理解。

引用大名的发言:

这种问题大神又怎么可能回答,人家不屑啊,所以我就不问

哈哈哈 ,还是忍不住答复一下,知道touch事件吗?

listeners = listeners.filter(l => l !== listener);
这一步把L看成1,我就说也不能这么写阿 :)

// 等同于
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}

======

这里写错了吧,这样的话 原来的reducer 比如doSomethingWithA 在定义外面这个reducer函数的时候就被调用了

Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。

View不能反推State,麻烦更正一下

醍醐灌顶,茅塞顿开????

条理清晰、结构合理,谢谢您写的教程。之前看了很多,但是第一次留言。谢谢????

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接