学习 Redux 是个十分痛苦的过程,因为你有可能不知道 Redux 和 React-Redux 是两个不同的东西,而且一堆看起来很新的概念总能把新手绕晕。
这篇文章将从一个简单的例子开始讲 Redux 到底要怎么使用。建议先学习 EventHub 或者看我的另一篇文章 React 的兄弟组件通信,因为 Redux 的主要思想就是 EventHub 思想。
纯 JS + Redux
先说下需求,首先要有一个数字,点击按钮数字加1,没了。不过这个过程要使用到 Redux,也就是说这个数字要放在全局变量中。
这里就用一个简单的 index.html 写就完了。HTML 代码如下:
接下来写 JS。
function add() {
store.dispatch({ type: 'add', payload: 1 }) // 1. dispatch 一个 action
}
function render() {
let app = document.querySelector('#app')
app.innerHTML = `
Clicked: ${store.getState()} times
`
}
function stateChanger(state, action) {
if (state === undefined) {
return 0
}
else {
if (action.type === 'add') {
let newState = state + action.payload
return newState // 2. 根据操作生成新的 state 触发一个事件
}
}
}
let store = Redux.createStore(stateChanger)
render(store)
store.subscribe(render) // 3. 接收到事件,重新 render
这里说明一下运行的步骤:
- 首先运行
render()
将标签都 append 到document
上,并在按钮上绑定add
回调 - 点击了按钮执行
add()
,然后 dispatch 一个 action(相当于触发了名为“add”的一个事件) - 因为在
Redux.createStore(stateChanger)
时,Redux 会监听 action(也就是事件)的触发,所以会执行stateChanger
里的代码 - 因为在
store.subscribe(render)
在 if-else 里修改了新的 state 后,Redux 会重新执行render()
函数,从而更新整个页面
Redux 的用法就是这么简单!但是为什么我们看官方档看得想死呢?因为加了 React,后来的所有看起来很变态的使用方法都是为了解决:怎么让代码更好分离,怎么获取全局变量等。思想还是那个思想。
React + Redux
现在我们正式加入 React,使用官方提供的create-react-app来创建应用。App 组件写成这样:
// App.js
class App extends Component {
add1() {
this.props.store.dispatch({ // 1. dispatch 一个 action
type: 'add',
payload: 1
})
}
render() {
return (
Clicked: {this.props.value} times
)
}
}
export default App
在入口文件去初始化 Redux:
const stateChanger = (state, action) => {
if (state === undefined) {
return 0
}
else {
if (action.type === 'add') {
let newState = state + action.payload
return newState // 2. 根据操作生成新的 state 触发一个事件
}
else {
return state
}
}
}
function render() {
ReactDOM.render(
,
document.getElementById('root')
)
}
const store = createStore(stateChanger)
store.subscribe(render) // 3. 接收到事件,重新 render
render()
感觉好像没什么变化呀,不就将一个 index.html 分成了 index.js 和 App.js 么?但是这里涉及到怎么去获取 store
的问题。
如果一两个组件就用 props
来传 store
就好了,但是如果组件很深,那么 store
就要像传家保一样一层层往下传,十分麻烦。
为了解决这个问题, React 社区又推出了一个新的工具:React-Redux。注意这个和 Redux 没有太大关系。为了说明这两个是不一样,我放下他们的官网:Redux 官网,React-Redux 官网。
React + Redux + React-Redux
先说明这个工具就只是用来让别的组件可以访问到 store 而已,所以它只有 4 个API,其中主要我们要用的就 2 个:Provider, connect。简单理解:
- Provider:将 store 放在顶层组件
- connect:将 store 里的数据和 dispatch action (也就是触发事件)和当前组件绑定,使得该组件可以自由访问和修改 store
现在修改上面两个文件:
// index.js
const stateChanger = (state, action) => {
if (state === undefined) {
return { n: 0 }
}
else {
if (action.type === 'add') {
let newState = {
n: state.n + action.payload
}
return newState
}
else {
return state
}
}
}
const store = createStore(stateChanger)
ReactDOM.render(
,
document.getElementById('root')
)
下面的 App.js
class App extends Component {
render() {
return (
Clicked: {this.props.n} times
)
}
}
// 将部分 store 里的 state 映射到 props 上
function mapStateToProps(state) {
return {
n: state.n
}
}
// 将 dispatch action 相关操作映射到 props 上
function mapDispatchToProps(dispatch) {
return {
add: () => {
dispatch({ type: 'add', payload: 1 })
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
来对比一下两份代码:
少了哪些?
- 不用
store.subscribe(render)
,Provider 会在更新值后自动重新渲染 - 不用在组件 App 里写回调函数了,在
mapDispatchToProps
里声明事件,这个事件会就负责调用dispatch(action)
的
多了哪些?
- 要在 App 组件外再去套一层
- 将 App 组件和 state,调用 dispatch 的函数连接起来。连接(映射)后的结果是,所有 state 数据和调用 dispatch 的函数都可以从
this.props
得到
这样就解决了组件访问,修改 store 里的 state 问题。唉,绕了一大圈就为了搞这些事。
那些变态概念
Redux 为什么这么难,是因为它将很简单的概念给了个新名字罢了。
Redux
- Store:存放全局数据的一个东西,但是要通过
store.getState()
来获取全局数据 - State:全局数据
- Action:对应 EventHub 里的事件,
actionType
就是事件名,payload
就是传入的数据 - Dispatch:触发事件,如
dispatch({ type: 'add', payload: 1})
,就是指触发一个名为“add”的事件,并带上了数据 1 - Reducers:对应触发事件后的回调函数,如我们平时听到的
onError = xxx
,这个 xxx 就是 Reducer,这个例子的 Reducer 就是stateChanger
React-Redux
- Provider:可以看成一个包住最外面 App 的一个标签,这个标签传入 store 后,通过某些方法所有组件都可以访问到 store
- mapStateToProps:将存在 store 里的数据放在这个组件的 props 上
- mapDispatchToProps:将需要调用 dispatch 的函数放在这个组件的 props 上
- connect:将上面两个东西和这组件联系起来
是不是很烦,嗯,我也是这么觉得的,明明很简单的概念,就是不好好说话。
(完)