前两篇教程介绍了 Redux 的基本用法和异步操作,今天是最后一部分,介绍如何在 React 项目中使用 Redux。
为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,本文主要介绍它。
这个库是可以选用的。实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范。
一、UI 组件
React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
UI 组件有以下几个特征。
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用
this.state
这个变量)- 所有数据都由参数(
this.props
)提供- 不使用任何 Redux 的 API
下面就是一个 UI 组件的例子。
const Title = value => <h1>{value}</h1>;
因为不含有状态,UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值。
二、容器组件
容器组件的特征恰恰相反。
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
总之,只要记住一句话就可以了:UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
你可能会问,如果一个组件既有 UI 又有业务逻辑,那怎么办?回答是,将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。
React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
三、connect()
React-Redux 提供connect
方法,用于从 UI 组件生成容器组件。connect
的意思,就是将这两种组件连起来。
import { connect } from 'react-redux' const VisibleTodoList = connect()(TodoList);
上面代码中,TodoList
是 UI 组件,VisibleTodoList
就是由 React-Redux 通过connect
方法自动生成的容器组件。
但是,因为没有定义业务逻辑,上面这个容器组件毫无意义,只是 UI 组件的一个单纯的包装层。为了定义业务逻辑,需要给出下面两方面的信息。
(1)输入逻辑:外部的数据(即
state
对象)如何转换为 UI 组件的参数(2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
因此,connect
方法的完整 API 如下。
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)
上面代码中,connect
方法接受两个参数:mapStateToProps
和mapDispatchToProps
。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state
映射到 UI 组件的参数(props
),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
四、mapStateToProps()
mapStateToProps
是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state
对象到(UI 组件的)props
对象的映射关系。
作为函数,mapStateToProps
执行后应该返回一个对象,里面的每一个键值对就是一个映射。请看下面的例子。
const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
上面代码中,mapStateToProps
是一个函数,它接受state
作为参数,返回一个对象。这个对象有一个todos
属性,代表 UI 组件的同名参数,后面的getVisibleTodos
也是一个函数,可以从state
算出 todos
的值。
下面就是getVisibleTodos
的一个例子,用来算出todos
。
const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) default: throw new Error('Unknown filter: ' + filter) } }
mapStateToProps
会订阅 Store,每当state
更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
mapStateToProps
的第一个参数总是state
对象,还可以使用第二个参数,代表容器组件的props
对象。
// 容器组件的代码 // <FilterLink filter="SHOW_ALL"> // All // </FilterLink> const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } }
使用ownProps
作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
connect
方法可以省略mapStateToProps
参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
五、mapDispatchToProps()
mapDispatchToProps
是connect
函数的第二个参数,用来建立 UI 组件的参数到store.dispatch
方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps
是一个函数,会得到dispatch
和ownProps
(容器组件的props
对象)两个参数。
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); } }; }
从上面代码可以看到,mapDispatchToProps
作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
如果mapDispatchToProps
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。举例来说,上面的mapDispatchToProps
写成对象就是下面这样。
const mapDispatchToProps = { onClick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter }; }
六、<Provider> 组件
connect
方法生成容器组件以后,需要让容器组件拿到state
对象,才能生成 UI 组件的参数。
一种解决方法是将state
对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state
传下去就很麻烦。
React-Redux 提供Provider
组件,可以让容器组件拿到state
。
import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp); render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
上面代码中,Provider
在根组件外面包了一层,这样一来,App
的所有子组件就默认都可以拿到state
了。
它的原理是React
组件的context
属性,请看源码。
class Provider extends Component { getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object }
上面代码中,store
放在了上下文对象context
上面。然后,子组件就可以从context
拿到store
,代码大致如下。
class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); // ... } } VisibleTodoList.contextTypes = { store: React.PropTypes.object }
React-Redux
自动生成的容器组件的代码,就类似上面这样,从而拿到store
。
七、实例:计数器
我们来看一个实例。下面是一个计数器组件,它是一个纯的 UI 组件。
class Counter extends Component { render() { const { value, onIncreaseClick } = this.props return ( <div> <span>{value}</span> <button onClick={onIncreaseClick}>Increase</button> </div> ) } }
上面代码中,这个 UI 组件有两个参数:value
和onIncreaseClick
。前者需要从state
计算得到,后者需要向外发出 Action。
接着,定义value
到state
的映射,以及onIncreaseClick
到dispatch
的映射。
function mapStateToProps(state) { return { value: state.count } } function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) } } // Action Creator const increaseAction = { type: 'increase' }
然后,使用connect
方法生成容器组件。
const App = connect( mapStateToProps, mapDispatchToProps )(Counter)
然后,定义这个组件的 Reducer。
// Reducer function counter(state = { count: 0 }, action) { const count = state.count switch (action.type) { case 'increase': return { count: count + 1 } default: return state } }
最后,生成store
对象,并使用Provider
在根组件外面包一层。
import { loadState, saveState } from './localStorage'; const persistedState = loadState(); const store = createStore( todoApp, persistedState ); store.subscribe(throttle(() => { saveState({ todos: store.getState().todos, }) }, 1000)) ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
完整的代码看这里。
八、React-Router 路由库
使用React-Router
的项目,与其他项目没有不同之处,也是使用Provider
在Router
外面包一层,毕竟Provider
的唯一功能就是传入store
对象。
const Root = ({ store }) => ( <Provider store={store}> <Router> <Route path="/" component={App} /> </Router> </Provider> );
(完)
羽风 说:
第三点的
三、conect()
拼写错了
2016年9月22日 09:49 | # | 引用
阮一峰 说:
@羽风:谢谢指出,已经改正。
2016年9月22日 11:16 | # | 引用
Will 说:
有一点不是特别明白:
store.subscribe(throttle(() => {
saveState({
todos: store.getState().todos,
})
}, 1000))
这里store.subscribe时,本来只要state有变化就会执行吧,为啥还要再使用throttle呢?
是有什么考虑吗?
2016年9月22日 11:34 | # | 引用
Will 说:
哦,我理解了,是怕性能问题,避免调用太频繁。
2016年9月22日 12:05 | # | 引用
冰冰 说:
简单明了,大赞~
2016年9月22日 14:46 | # | 引用
Min 说:
‘容器组件则是由 React-React 自动生成’
这里应该是React-Redux
2016年9月22日 17:19 | # | 引用
爱吃土豆 说:
Redux 入门教程(三)真是通俗易懂,让我茅塞顿开,给阮老师点个赞!么么哒
2016年9月22日 17:37 | # | 引用
阮一峰 说:
@Min:谢谢指出,已经改正。
2016年9月22日 19:48 | # | 引用
拖鞋酱 说:
之前在github上看了好几个入门教程,都不是很明白.
当时flux就是从阮老师这儿看懂的.给阮老师点赞.
2016年9月27日 21:56 | # | 引用
迹忆 说:
给阮老师点个赞
2016年9月29日 16:37 | # | 引用
丢丢 说:
峰哥可以搞一个大赏功能,想给你大赏都不知道往哪打
2016年9月30日 10:35 | # | 引用
小媒体 说:
很赞,多谢!阮老师的页面风格看着都比较权威。
2016年9月30日 16:10 | # | 引用
McDu 说:
2016年10月 5日 18:12 | # | 引用
少安 说:
求下一篇有 react redux 后台渲染的文章啊
2016年10月11日 17:28 | # | 引用
tangyz 说:
最后使用Provider在根组件外面包一层的代码,用的是todo的,不是计算器的了
2016年10月11日 18:02 | # | 引用
BigBrother5 说:
感谢阮老师,茅塞顿开!
2016年10月14日 17:50 | # | 引用
bbfish 说:
支持,确实比其它的资料更容易懂
2016年10月14日 18:33 | # | 引用
Kimi 说:
七:实例 计数器中
2016年10月15日 01:01 | # | 引用
山水一程 说:
React-Redux提供的例子:https://github.com/jackielii/simplest-redux-example 是用python运行的。我把例子代码放到webpack环境下了,地址:https://github.com/cag2050/react_redux_demo161019 ,需要的同学,可以看下。
2016年10月19日 19:15 | # | 引用
chris 说:
请问store.subscribe和mapStateToProps有什么关系吗?
2016年10月20日 00:01 | # | 引用
刘俊杰 说:
阮一峰老师每篇关于react及其技术栈的文章都拯救了我。。!!!!!
2016年10月20日 14:03 | # | 引用
lemonleo 说:
阮老师好~ 想请教个问题: 当组件第一次渲染的时候 并没有看见哪里触发了dispatch,那是怎么更新的UI呢? 是react默认第一次会自动触发dispatch吗?
2016年10月24日 21:14 | # | 引用
steve young 说:
是在高阶组件(connect 函数返回的组件)里的 componentDidMount 订阅 store 更新,和 componentWillUnmount 取消订阅。(还有声明 contextTypes...)
安利下我的学习笔记...~= ̄ω ̄=~
http://buptsteve.github.io/blog/2016/10/25/7.react-and-redux-learning-note-basics/
2016年10月26日 23:09 | # | 引用
test 说:
AddTodo
像这种需要将input的内容dispatch出去的要如何划分成ui组件和容器组件呢?
2016年10月30日 12:38 | # | 引用
chris 说:
感谢,关于官网的real-world示例,有一些问题,不知道可否请教下~
2016年11月 8日 01:27 | # | 引用
Grubby 说:
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
这一句的话,箭头函数返回一个对象对象不用()包起来不会报错吗?
2016年11月 9日 15:03 | # | 引用
朱砂变 说:
mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
阮老师,在react-redux v4.4.5版本的connect函数中,并没有找到mapStateToProps订阅Store的行为,请问您上面说的那句话该如何理解?
2016年11月12日 16:48 | # | 引用
czj 说:
比如一个简单的状态 现实隐藏某个组件 类似这种状态也要放到给redux管理吗 这个应该属于ui组件 具体在项目中应该如何划分呢?
2016年11月20日 20:27 | # | 引用
jarson 说:
阮老师:mapStateToProps使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
var num = 0;
ReactDom.render((
<Provider store={store}>
<MyCounter num={num}/>
</Provider>
), document.getElementById('app'));
setTimeout(function() {
num =1;
}, 1000)
我这样测,发现,页面并没有渲染呀。希望老师抽空能解答一下,不胜感激!
2016年12月20日 11:41 | # | 引用
风一样的男人 说:
redux大法看的好吃力
2016年12月28日 01:59 | # | 引用
Peter 说:
乏味,且不明所以。感觉作者自己也不懂
2017年1月 2日 22:58 | # | 引用
Andy 说:
感谢阮老师的付出,阮老师的博文总是清晰易懂。
阮老师为什么不在文章末尾放个支付宝或者微信的二维码呢?我想给阮老师买杯咖啡☕️ ~ 相信很多人也会有这想法的,毕竟我们从阮老师这里学到了很多~
2017年1月 7日 16:12 | # | 引用
qinicy 说:
最近对React-Native大感兴趣,大概地撸了一遍阮老师的博客,虽然不是前端出身,但理解起来不难,好文章。
2017年1月13日 16:33 | # | 引用
高峰 说:
第三章说了异步操作,可是你 react-redux 里面没有说,怎么异步操作啊。是参照第二章吗?我现在使用react-redux 请教,怎么选择异步fetch 返回数据啊。
2017年1月16日 12:05 | # | 引用
lilieming 说:
如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件props 中。
2017年1月19日 21:10 | # | 引用
nicemayi 说:
请问阮老师, Counter例子中的createStore代码:
const store = createStore(
todoApp,
persistedState
);
是不是有问题? createStore接受的第一个参数应该是reducer(本例中是counter), 而不是UI组件。
2017年2月10日 03:46 | # | 引用
张凯 说:
总觉得代码部分用粉红有点突兀,影响阅读,颜色是否可以弱化点
2017年2月18日 23:50 | # | 引用
LeoEatle 说:
这里的createStore确实应该是传参数名错了
2017年2月20日 14:12 | # | 引用
张铮 说:
虽然没太看懂,打算再看一遍 ,谢谢阮老师的良心教程
2017年2月22日 23:09 | # | 引用
sophie 说:
const store = createStore(
todoApp,
persistedState
);
这里的确不是Counter的代码,源代码大家可以看阮老师的源代码,应该是这样的:
// Store
const store = createStore(counter);
2017年3月 1日 15:18 | # | 引用
黎龙 说:
收益匪浅啊,感谢阮老师。
2017年3月 3日 17:50 | # | 引用
sakop 说:
谈一下我阅读了阮老师三篇文章后的理解,很有可能是有偏差的,不足请指出,谢谢!
阮老师上文中关于Provider的解释,缺少了对context的介绍,我补充一下吧:
为了让子组件能够获得context属性,React强制要求根组件(此处为Provider组件)提供getChildContext实例方法,以及类属性childContextTypes。而子组件想要获取context,也必须定义类级别的Counter. contextTypes属性。定义是双向的,如果缺少了任何一块,子组件都获取不到context属性。
我认为父组件的那块定义是在Provider的代码中实现的,而子组件的那部分是在connect方法中实现的。
因此connect方法为Counter组件添加的context属性实质上是由Provider传下来的,这样在mapStatesToProps方法里的state参数实质上就是this.context.store.getState()方法获得的。
然后看一下页面首次加载以及之后有互动行为之后整个逻辑的流程:
当第一次渲染页面时,store里的初始state是怎么获得的呢?
代码一开始一般就是
createStore(reducers,defaultParams)的调用,其中reducers可以使一个reducer,也可是redux.combineReducers过的reducer的集合。
createStore方法会对每个reducer去dispatch一个action.type=@@redux/INIT类型的action,而这个action一般在reducer的代码里不会被handle,直接掉入default块,于是就返回了state的初始状态。
然后一般就会ReactDom.render(将应用渲染出来,每个子组件的容器组件通过传入this.context.store.getState()方法获得的state对象, 以及容器组件上自带的ownProps给mapStatesToProperties方法,来构建props,最后将props应用到子组件的UI组件上。
当在子组件上发生交互行为,如click时,mapDispatchToProps会定义click触发时应该dispatch哪一个action的映射。
然后store接收到这个action后会进行reduce,得到最新的state,然后再调用所有的子组件的mapStatesToProps方法生成新的props。
最后对Provider进行重新渲染,当然上面的事件计算出来的很多state可能都不会发生变化,所以diff算法不会去修改这些没有发生变化的组件,因此性能也比较好。
2017年3月14日 16:02 | # | 引用
捕捉流星的孩子 说:
疑问开头的UI和container的划分:
(UI只负责呈现,不包含业务逻辑;container只负责业务逻辑不包含UI呈现)
对于一个页面, 真的能划分只有UI和container两种组件吗?
假设一个页面对应一个container,我的情况是总会出现在页面级container下混合UI+其他container。。
求解。。
2017年3月14日 18:11 | # | 引用
阿范 说:
请教大神或各位个问题,
在ui组件中,如果行为中需要传参该怎么做?
比如目前是 ;
但比如有多个button,需要sendClickAction时传递入参id,怎么做?
2017年3月16日 10:40 | # | 引用
前端小白白 说:
三篇看下来有点累,什么时候歇好了再来看,一峰老师写得好!
2017年3月22日 21:08 | # | 引用
皖林 说:
五的最后:
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
onClick的返回值应该加上()。({type: xxx})
2017年3月31日 17:44 | # | 引用
finalljx 说:
建议阮一峰老师增加子组件如何通过context 获取到store的样例,对新手有较大帮助,我是看了官网文档之后才知道方法.
https://facebook.github.io/react/docs/context.html
2017年4月18日 15:31 | # | 引用
Woody 说:
貌似官方不建议使用context。
It is an experimental API and it is likely to break in future releases of React.
2017年4月21日 13:11 | # | 引用
Ajaxyz 说:
阮大神讲的果然通俗易懂,解决了我多年的困惑!感谢老师
2017年5月11日 16:29 | # | 引用
weijie 说:
然道就我一个人看不懂嘛....
2017年5月12日 10:43 | # | 引用
xujun 说:
懵逼中。。。。继续第二遍
2017年5月17日 16:23 | # | 引用
Neil 说:
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
这个函数是不是应该写成
const mapDispatchToProps = {
onClick: (filter) => ({
type: 'SET_VISIBILITY_FILTER',
filter: filter
});
}
箭头函数直接返回对象的话要加上括号
2017年6月 6日 17:54 | # | 引用
a 说:
const { store } = this.context;
然而并不行
求ruan老师贴代码的时候保证可执行,免得误导人!
2017年6月12日 16:24 | # | 引用
cc 说:
最近在学react,看了很多,就这篇最有效,讲的太到位了。尤其是redux底层的原理,简直不能更赞。
2017年6月27日 23:17 | # | 引用
花自飘凌水自流 说:
好屌啊!看着阮大神的代码,长大的!
2017年6月28日 13:28 | # | 引用
suvllian 说:
获益匪浅
2017年7月12日 13:48 | # | 引用
civil 说:
<Provider store={store}>
<App />
</Provider>
App所有子组件无法获取到store,因为this.context空值,请大师指教
2017年7月27日 18:24 | # | 引用
林047 说:
react 的props不是应该放一些不变的东西吗?那现在右container的state每次提供不同的数据,不是和react的props设计思想有出入吗
2017年9月 1日 11:12 | # | 引用
zyh 说:
你是托把
2017年9月 1日 17:13 | # | 引用
秋梦槿 说:
有人能帮我看下我的这个问题吗
是不是reducers写的有问题,
这是效果 https://amanzyw.github.io/react-redux/dist/
项目地址在:https://github.com/amanzyw/react-redux/tree/gh-pages
2017年9月23日 10:05 | # | 引用
健健 说:
似懂非懂,写个demo先
2017年10月18日 16:41 | # | 引用
webLion200 说:
阮老师,在实例的第四行文字解释是不是写错了?
“接着,定义value到state的映射,以及onIncreaseClick到dispatch的映射。”
应该是 “定义state到value的映射”吧
2017年10月26日 20:02 | # | 引用
爱吃鱼的猫 说:
讲解的非常细致,自己看文档很多东西走马观花,理解不透彻。做项目的时候容易出现问题,再看看阮老师的文档有种茅塞顿开的感觉。
2017年10月27日 19:08 | # | 引用
nick 说:
阮老师 果然厉害 学习redux 好几天都在蒙圈中。看来后立马感觉豁然开朗。
2017年11月 7日 17:33 | # | 引用
nanxiaobei 说:
这个代码放到 IDE 里就会提示 error,可能是阮老并不用 IDE ...
2017年11月16日 15:57 | # | 引用
银桑 说:
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
此处应该改为:
const mapDispatchToProps = ({
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
}
})
去掉一个;和加了括号返回对象
2017年11月17日 16:24 | # | 引用
龙森 说:
现在看阮老师的博客学习新技术,都成必须课了。赞一个!
2017年11月23日 23:42 | # | 引用
小冶 说:
第六节多处提到context、子组件用到的state是不是写错了,应是store?
2017年12月 9日 23:34 | # | 引用
Decade 说:
非常感谢,看着看着就感觉懂了很多了
2018年1月16日 17:06 | # | 引用
me 说:
js不太好,学习react感觉好难啊,哪位大神有指导意见?
2018年2月27日 15:53 | # | 引用
youenough 说:
老师,我运行了下redux examples下的real-world示例,本来一直有个困扰就是路由后怎么记住页面位置,然后发现这个示例可以记住每个路由地址的页面位置,但代码中看不懂是怎么实现的?
2018年3月 1日 00:21 | # | 引用
pengfuxiao 说:
官方文档有说的, 没设置组件的 contextTypes,context 是空对象。你先搞清楚 Provider 是如何实现的,然后用connect,就可以省去这一步。
2018年3月 2日 12:53 | # | 引用
听说 说:
阮老师讲的挺好的,昨天晚上看了一些视频,有些懵,今天看到这篇文章,有些东西也理解了一点。
2018年3月 8日 11:23 | # | 引用
Node 说:
写的太好了!!
2018年3月29日 19:10 | # | 引用
GoTaKu 说:
阮老师,六、 组件中前半部分store写成state了
2018年4月 2日 19:44 | # | 引用
冉冉 说:
这三个blog基本涵盖了我的工作内容;一开始来看没看懂,工作了一个月再来看 像知识点梳理总结,太赞了! 第二篇中间件的thunk还是不太懂;T.T
2018年5月 6日 14:30 | # | 引用
汤圆 说:
很感谢你的教程,好多东西感觉都是从你这里入门,确实比其他的容易看懂
2018年5月16日 16:43 | # | 引用
Ming 说:
我用容器等结构也写了个,components,containers,actions,reducers
https://github.com/hemingming/redux-counter
2018年5月17日 17:19 | # | 引用
Rookies 说:
你好 我想问下 关于 redux 是从新封装过redirect? 为什么我在redux下的react中 直接导入redirect 但是不识别呢
2018年6月28日 14:08 | # | 引用
wangqi 说:
看到示例代码开始懵逼
2018年9月12日 11:29 | # | 引用
赵应龙 说:
Redux 第一篇文章很好懂,后两篇感觉略难懂。哈哈哈????,我会好好学习的。
2018年11月16日 01:11 | # | 引用
王福宗 说:
阮老师,你怎么不弄个打赏码,这组文章前前后后读了3遍,太有帮助了!
2018年11月24日 22:43 | # | 引用
张三の哥 说:
阮老师写的太棒了!看完之前不理解的,现在明白了!
2018年12月18日 16:15 | # | 引用
yjy 说:
计数器的小案例代码不知道该怎么放,有文件的目录结构吗?全部都是在一起的,不知道该写进去进行执行。
2018年12月21日 15:45 | # | 引用
jiejie 说:
大佬写的真好!很容易懂!
2019年10月25日 17:54 | # | 引用
Jeremy 说:
写的真是不好,看完就觉得是在浪费时间
2020年1月 5日 21:06 | # | 引用
Joash 说:
看你的评论觉得是在浪费时间
2020年7月13日 14:09 | # | 引用
钱成 说:
const mapDispatchToProps = {
onClick: (filter) => {//此处的filter应该是ownProps
type: 'SET_VISIBILITY_FILTER',
filter: filter//此处的filter应该是ownProps.filter
};
}
2020年7月23日 10:44 | # | 引用
范志林 说:
看完入门视频动手打一打代码再看这个文章就理解了。connect第一个参数主要从上层组件传递参数到下层组件,而上层组件<Provider>的状态参数来源于store,而reducer则是这个store的操作者,负责更新store内的状态state;第二个参数作用主要是组件比如要点击按钮触发事件时,如何将这些改变告诉给reducer然后进行状态state更新,这些改变会封装为一个action发送到reducer。
2020年11月 4日 16:15 | # | 引用
留果 说:
有一个语法错误:
五、mapDispatchToProps
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
应改为
const mapDispatchToProps = {
onClick: (filter) => ({
type: 'SET_VISIBILITY_FILTER',
filter: filter
})
}
2020年11月17日 14:01 | # | 引用
baooab 说:
下面的代码:
```
const store = createStore(
todoApp,
persistedState
);
```
createStore 函数第一个参数,应该传入 counter,而非 todoApp。
2020年11月29日 19:04 | # | 引用
大名 说:
这才是提高生产力的东西,这才是编程该有的样子,数着字节设中断的以后可能就找不到工作了
2021年9月27日 21:15 | # | 引用
Blazar 说:
阮老师,我的超人!!终于学会redux了,要是能给老师打赏就好了
2022年3月18日 14:13 | # | 引用