Redux实例学习之计数器

创建counter项目

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(
    <App/>,
    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 (
            <div>
                <p><span style={{fontSize: '50px'}}>0</span></p>
                <p>
                    <button style={{fontSize: '30px', 'width': '50px', 'height': '50px'}} onClick={this.increase}> +
                    </button>
                     
                    <button style={{fontSize: '30px', 'width': '50px', 'height': '50px'}} onClick={this.decrease}> -
                    </button>
                </p>
            </div>
        );
    }
}

安装redux

yarn add redux

安装后,启动:yarn start,运行效果如图:

得到当前状态

目前页面上显示的数字是写死的,而实际应该从状态中获取,这样方便我们做加减法时修改状态,使页面上的数字得到自动更新。
redux的状态都是保存在store对象之中的,所以我们要做的第一步是引入redux,并创建store对象。
app.js第二行添加如下代码:

import {createStore} from 'redux'

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

  • Store
    Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 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()拿到。

当刷新页面时,观察控制台,可以看到如下结果:

可以看到,页面在渲染后,打印了stateaction两个参数,其中state的值为undefinedaction为一个对象,该对象有一个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
    Action 是一个对象,该对象包含多个属性,其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置,社区有一个规范可以参考。
    Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store

现在我们把页面中(app.js)写死的0替换为当前状态
将以下代码

<p><span style={{fontSize: '50px'}}>0</span></p>

替换为:

<p><span style={{fontSize: '50px'}}>{store.getState()}</span></p>

此时,页面上显示的数字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

  • 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)

然后根据actiontype属性来决定是做加法还是减法,根据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,没有变化。这是需要调用storesubscribe方法来设置监听,一旦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(
    <App store={store}/>,
    document.getElementById('root')
)

store.subscribe(() => {
    ReactDOM.render(
        <App store={store}/>,
        document.getElementById('root')
    )
})

通过<App store={store}/>像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 (
            <div>
                <p><span style={{fontSize: '50px'}}>{store.getState()}</span></p>
                <p>
                    <button style={{fontSize: '30px', 'width': '50px', 'height': '50px'}} onClick={this.increase}> +
                    </button>
                     

                    <button style={{fontSize: '30px', 'width': '50px', 'height': '50px'}} onClick={this.decrease}> -
                    </button>
                </p>
            </div>
        );
    }
}

现在可以根据状态自动刷新网页了,效果如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值