react-redux基本使用

react-redux基本使用

1.redux

1.1 redux运行流程

react-redux基本使用_第1张图片

1.1.1 actionCreators

actionCreators是redux运行的第一个角色,他将用户将要操作的内容包装成一个对象,{key:value},包装成一个{type:data},type为操作的类型,而data是操作具体的数值

1.1.2 store

store为redux最为核心的角色,虽然他接受actionCreators创造的对象,但实质并不是store去处理,而是store交给了他手下的reducers去处理,reducer处理完交给store保存和更新状态

1.1.3 reducers

reducer是redux整个工作中,处理状态的工作者,状态的处理最后都是交给了reducer去处理


2. 安装redux

在一个react-cli项目中,打开终端,输入

npm i redux

注意:redux并不是react官方创作,只不过redux和react配合更加出众,甚至在Vue以及Angular都可以用redux去管理状态,但在Vue中Vue开发者团队打造了一个更适合Vue的Vuex

3.在react中操作redux


3.1 创建最为核心的store

redux在底层采用了分别暴露的方式

import {createStore} from "redux"

export default createStore("这里要放入reducer")

3.2 创建为store工作的reducer

import {INCREMENT, DECREMENT} from "../constant"

const initPre = 0
export default function countReducer(preState = initPre, action) {
    const {type, data} = action
    switch (type) {
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    }
}

reducer函数可以接受两个参数,第一个参数是原状态,第二个参数是action对象,注意在一开始reducer可以初始化,在es6中可以在定义形参时传入默认值,在reducer初始化时是需要原状态,这里定义了一个变量initPre,用来指定state的初始值为0

3.3 redux的响应式处理

在上述代码基础上我们就创建了一个最简版的redux,但redux状态的改变并不能引起页面的变化,那是因为react没有监视redux状态的变化,所以我们要在index.js中加入redux的监听

store.subscribe(() => {
    root.render(
        <React.StrictMode>
            <Provider store={store}>
                <App/>
            </Provider>
        </React.StrictMode>
    )
})

这样redux的状态修改就可以引起react发生重新渲染


4.完整版redux

再用react-redux之前我们要完善actionCreators
上述说过actionCreators的功能是创建action对象,我们之前可以手动的创建action对象,但actionCreators可以完成

import {INCREMENT, DECREMENT} from "../constant"

const createIncrementAction = data => ({type: INCREMENT, data})
const createDecrementAction = data => ({type: DECREMENT, data})
const createIncrementAsyncAction = (data, time) => dispatch => {setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time)
}

export {createDecrementAction, createIncrementAction, createIncrementAsyncAction}

4.1 redux异步action

redux不仅为我们同步生成action的方法,还提供了异步action,比如像ajax,计时器这样的副作用我们可以运用异步action

const createIncrementAsyncAction = (data, time) => dispatch => {setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time)
}

4.2 thunk中间件

在redux中我们是不能让action成为一个函数,但在thunk中间件的基础上我们就可以redux支持异步函数式的action

4.2.1安装thunk


npm i thunk

4.2.2 在创建store时引用中间件

import {createStore, applyMiddleware} from "redux"
import thunk from "redux-thunk"

export default createStore(countReducer,applyMiddleware(thunk))

4.3 redux异步action

在这时redux就可以支持异步action

5. react-redux


5.1 react-redux的流程图

react-redux基本使用_第2张图片

5.2 Count容器组件


5.2.1 容器组件

容器组件时redux和reactUI组件的中间桥梁,容器组件拿到redux的状态然后通过props的方法传给reactUI组件使用,如果UI组件要修改状态,同样也要通过容器组件,再由容器组件交给redux处理

5.2.2 创建容器组件

import React, {Component, Fragment} from "react"
import {connect} from "react-redux"
import {createDecrementAction, createIncrementAction, createIncrementAsyncAction} from "../../redux/action/count"

class Count extends Component {

    increment = () => {
        const {value} = this.selectNumber
        this.props.increment(parseInt(value))
    }

    decrement = () => {
        const {value} = this.selectNumber
        this.props.decrement(parseInt(value))
    }

    incrementIfOdd = () => {
        if (this.props.count % 2 !== 0) {
            const {value} = this.selectNumber
            this.props.increment(parseInt(value))
        }
    }
    incrementAsync = () => {
        const {value} = this.selectNumber
        this.props.incrementAsync(parseInt(value), 500)
    }

    render() {
        return (
            <Fragment>
                <h1>Count组件,Person组件的人数为{this.props.person.length}</h1>
                <h2>当前的和为{this.props.count}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value={1}>1</option>
                    <option value={2}>2</option>
                    <option value={3}>3</option>
                </select>&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当和为奇数时加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
                &nbsp;
            </Fragment>
        )
    }
}

const mapStateToProps = state => ({count: state.count, person: state.person})

const mapDispatchToProps = dispatch => ({
    increment: number => dispatch(createIncrementAction(number)),
    decrement: number => dispatch(createDecrementAction(number)),
    incrementAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time))
})

const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count)

export default CountContainer

5.2.3 connect函数

react-redux为我们提供这个函数,来构建容器组件,从而构建桥梁,connect运用了函数的柯理化技术,可以为UI组件传递redux的状态以及操作状态的方法


mapStateToProps作为调用柯理化函数的第一个参数,该方法的作用是为UI组件提供redux的状态

mapDispatchToProps作为调用柯理化函数的第二个参数,该方法为UI组件提供操作状态的方法

5.3 Provider


我们可以用react-redux给我们提供的一个标签,为后代的UI组件自动分配store,来到index.js中

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from "./redux/store";
import {Provider} from "react-redux"

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>
);
reportWebVitals();

5.4 注意

在之前我们要手动添加针对redux的状态的监听,但引入react-redux之后我们不需要再添加,因为react-redux为我们在底层做了处理

6.redux开发者工具


6.1 开发者工具

我们可以在浏览器的扩展里下载redux开发者工具,从而实现更好的开发体验
react-redux基本使用_第3张图片

6.2通过代码触发开发者工具


6.2.1 redux-devtools-extension

安装redux-devtools-extension

npm i redux-devtools-extension

6.2.2在创建store时触发开发这工具

import {createStore, applyMiddleware, combineReducers} from "redux"
import countReducer from "./reducer/count"
import personReducer from "./reducer/person";
import {composeWithDevTools} from "redux-devtools-extension"
import thunk from "redux-thunk"


const allReducer = combineReducers({count: countReducer, person: personReducer})
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))


7.多个reducer处理

在创建store时我们不做特殊处理的化我们只能引用一个reducer

这时我们就需要一个api—combineReducers进行整合

import {createStore, applyMiddleware, combineReducers} from "redux"
import countReducer from "./reducer/count"
import personReducer from "./reducer/person";
import {composeWithDevTools} from "redux-devtools-extension"
import thunk from "redux-thunk"


const allReducer = combineReducers({count: countReducer, person: personReducer})
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))

8.源码

8.1 容器组件Count

import React, {Component, Fragment} from "react"
import {connect} from "react-redux"
import {createDecrementAction, createIncrementAction, createIncrementAsyncAction} from "../../redux/action/count"

class Count extends Component {

    increment = () => {
        const {value} = this.selectNumber
        this.props.increment(parseInt(value))
    }

    decrement = () => {
        const {value} = this.selectNumber
        this.props.decrement(parseInt(value))
    }

    incrementIfOdd = () => {
        if (this.props.count % 2 !== 0) {
            const {value} = this.selectNumber
            this.props.increment(parseInt(value))
        }
    }
    incrementAsync = () => {
        const {value} = this.selectNumber
        this.props.incrementAsync(parseInt(value), 500)
    }

    render() {
        return (
            <Fragment>
                <h1>Count组件,Person组件的人数为{this.props.person.length}</h1>
                <h2>当前的和为{this.props.count}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value={1}>1</option>
                    <option value={2}>2</option>
                    <option value={3}>3</option>
                </select>&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当和为奇数时加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
                &nbsp;
            </Fragment>
        )
    }
}

const mapStateToProps = state => ({count: state.count, person: state.person})

const mapDispatchToProps = dispatch => ({
    increment: number => dispatch(createIncrementAction(number)),
    decrement: number => dispatch(createDecrementAction(number)),
    incrementAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time))
})

const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count)

export default CountContainer

8.2 容器组件Person

import React, {Component, Fragment, PureComponent} from "react"
import {nanoid} from "nanoid"
import {connect} from "react-redux"
import {createAddPersonAction} from "../../redux/action/person"

class Person extends Component {

    addPersons = () => {
        const name = this.nameNode.value
        const age = this.ageNode.value
        const personObj = {id: nanoid(), name, age}
        this.props.addPerson(personObj)
        this.nameNode.value = ""
        this.ageNode.value = ""
    }

    render() {
        return (
            <Fragment>
                <h1>Person组件,Count组件的和为{this.props.count}</h1>
                <input placeholder="输入名字" type="text" ref={c => this.nameNode = c}/>&nbsp;
                <input placeholder="输入年龄" type="text" ref={c => this.ageNode = c}/>
                <button onClick={this.addPersons}>添加</button>
                <Length per={this.props.person}/>
                <ul>
                    {
                        this.props.person.map(p => {
                            return <li key={p.id}>{p.name}----{p.age}</li>
                        })
                    }
                </ul>
            </Fragment>
        )
    }
}

class Length extends PureComponent {
    render() {
        return (
            <Fragment>
                <h2>人数为:{this.props.per.length}</h2>
            </Fragment>
        )
    }
}

export default connect(
    state => ({person: state.person, count: state.count}),
    {
        addPerson: createAddPersonAction
    }
)(Person)

8.3 count_action.js

import {INCREMENT, DECREMENT} from "../constant"

const createIncrementAction = data => ({type: INCREMENT, data})
const createDecrementAction = data => ({type: DECREMENT, data})
const createIncrementAsyncAction = (data, time) => dispatch => {setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time)
}

export {createDecrementAction, createIncrementAction, createIncrementAsyncAction}

8.4 person_action.js

import {ADDPERSON} from "../constant"

export const createAddPersonAction = (personObj) => ({type: ADDPERSON, data: personObj})

8.5 count_reducer.js

import {INCREMENT, DECREMENT} from "../constant"

const initPre = 0
export default function countReducer(preState = initPre, action) {
    const {type, data} = action
    switch (type) {
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    }
}

8.6 person_reducer.js

import {ADDPERSON} from "../constant"

const initState = []
export default function personReducer(preState = initState, action) {
    const {type, data} = action
    switch (type) {
        case ADDPERSON:
            return [...preState, data]
        default:
            return preState
    }
}

8.7store.js

import {createStore, applyMiddleware, combineReducers} from "redux"
import countReducer from "./reducer/count"
import personReducer from "./reducer/person";
import {composeWithDevTools} from "redux-devtools-extension"
import thunk from "redux-thunk"


const allReducer = combineReducers({count: countReducer, person: personReducer})
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))

8.8 constant.js

const INCREMENT = "increment"
const DECREMENT = "decrement"
const ADDPERSON = "addPerson"

export {INCREMENT, DECREMENT, ADDPERSON}

8.9 app.js

import {Fragment} from "react"
import './App.css';
import Count from "./containers/Count";
import Person from "./containers/Person";


function App() {
    return (
        <Fragment>
            <Count/>
            <br/>
            <Person/>
        </Fragment>
    );
}

export default App;

8.10 index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from "./redux/store";
import {Provider} from "react-redux"

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>
);
reportWebVitals();

你可能感兴趣的:(JavaScript,前端,javascript,前端,react.js)