create-react-app counter
删除多余文件,src目录只保留app.js和index.js
index.js内容如下:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
,
document.getElementById('root')
);
app.js内容如下:
import React from 'react'
export default class App extends React.Component {
increase = () => {
console.log('+1')
}
decrease = () => {
console.log('-1')
}
render() {
return (
0
);
}
}
yarn add redux
目前页面上显示的数字是写死的,而实际应该从状态中获取,这样方便我们做加减法时修改状态,使页面上的数字得到自动更新。
redux的状态都是保存在store
对象之中的,所以我们要做的第一步是引入redux,并创建store对象。
在app.js
第二行添加如下代码:
import {createStore} from 'redux'
Redux 提供
createStore
这个函数,用来生成Store
。
接着在下一行创建store对象:
const store = createStore((state,action)=>{
console.log('state:', state)
console.log('action:', action)
return state
})
createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。接受的函数中有两个入参,state和action,state代表当前的状态,action为当前的动作
当前时刻的State
,可以通过store.getState()
拿到。
当刷新页面时,观察控制台,可以看到如下结果:
可以看到,页面在渲染后,打印了state
和action
两个参数,其中state
的值为undefined
,action
为一个对象,该对象有一个type
属性,值为@@redux/INITe.m.x.f.8.p
如果我们希望state的初始值是0,那么可以给state这个参数加上默认值。
const store = createStore((state = 0, action) => {
console.log('state:', state)
console.log('action:', action)
return state
})
Action
是一个对象,该对象包含多个属性,其中的type
属性是必须的,表示 Action
的名称。其他属性可以自由设置,社区有一个规范可以参考。Action
描述当前发生的事情。改变 State
的唯一办法,就是使用 Action
。它会运送数据到 Store
。现在我们把页面中(app.js)写死的0替换为当前状态
将以下代码
0
替换为:
{store.getState()}
此时,页面上显示的数字0就是从状态中获取的了。
现在我们希望点击加号后,改变状态值,使其结果变为1。
前面说了,改变 State
的唯一办法,就是使用 Action
。
在increase
函数中,创建一个action对象,内容如下:
const action = {
type: 'increment',
number: 1
}
这个action包含了两个属性,其中type
属性是必须的,内容为increment
,代表当前要执行的是加
这个动作。number
是我们自己取的属性名(可以改成其他的),值为1,代表执行加这个动作后,当前状态需要加上1。
前面提到所有的数据都保存在store对象,要完成加法,需要先从store中取得当前状态值,并加上action.number
的值,最后把结果存入当前状态。
与之前react通过this.setState来更新状态不同,store并没有setState这个方法。store提供了dispatch
方法来把action传递出去。
increase = () => {
const action = {
type: 'increment',
number: 1
}
store.dispatch(action)
}
现在点击页面上的加号按钮,观察控制台,可以看到如下结果:
可以看到,当点击了加号后,action对象被传递到了createStore
接收的函数参数这里。这个函数被称为Reducer
。
Store
收到 Action
以后,必须给出一个新的 State
,这样 View 才会发生变化。这种 State
的计算过程就叫做 Reducer
。Reducer
是一个函数,它接受 Action
和当前 State
作为参数,返回一个新的 State
。const store = createStore((state = 0, action) => {
console.log('state:', state)
console.log('action:', action)
return state
})
改写为:
const reducer = (state = 0, action) => {
console.log('state:', state)
console.log('action:', action)
return state
}
const store = createStore(reducer)
然后根据action
的type
属性来决定是做加法还是减法,根据number
属性来决定加多少和减多少。
进一步完善代码为:
const reducer = (state = 0, action) => {
console.log('state:', state)
console.log('action:', action)
switch (action.type) {
case 'increment':
return state + parseInt(action.number)
case 'decrement':
return state - parseInt(action.number)
default:
return state
}
}
可以看到state已发生了变化。
观察控制台可以看到每次点击加号后,state都发生了变化,但是页面上的数字却还是0,没有变化。这是需要调用store
的subscribe
方法来设置监听,一旦state发生变化,就自动执行这个函数。
在以下代码:
const store = createStore(reducer)
下方添加如下代码:
store.subscribe(() => {
console.log('监听状态:',store.getState())
})
可以看到每次点击后,state发生了变化的同时,redux都会自动调用store的subscribe方法。也就是说,我们只需要把ReactDOM.render
写在subscribe方法里就可以实现state更新后view的自动刷新了。
由于ReactDOM.render
是写在index.js
中的,所以store对象也应该写到index.js里。
改写后的index.js代码如下:
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import App from './App'
const reducer = (state = 0, action) => {
console.log('state:', state)
console.log('action:', action)
switch (action.type) {
case 'increment':
return state + parseInt(action.number)
case 'decrement':
return state - parseInt(action.number)
default:
return state
}
}
const store = createStore(reducer)
ReactDOM.render(
,
document.getElementById('root')
)
store.subscribe(() => {
ReactDOM.render(
,
document.getElementById('root')
)
})
通过
像app.js传递store对象。
改写后的app.js内容如下:
import React from 'react'
export default class App extends React.Component {
increase = () => {
const {store} = this.props
const action = {
type: 'increment',
number: 1
}
store.dispatch(action)
}
decrease = () => {
const {store} = this.props
const action = {
type: 'decrement',
number: 1
}
store.dispatch(action)
}
render() {
const {store} = this.props
return (
{store.getState()}
);
}
}