Redux基础

在这里插入图片描述

一种管理状态的js工具。store数据仓库,数据和状态都放在这里,集中管理。

有四个部分。React Components就相当于大官人,然后我们去作“大宝剑”,我们先见到的是Action Creators“妈妈桑”,我们说我要找小红,我是熟客了。"妈妈桑"就回到了Store,然后让Reducer看看"小红“忙不忙(现在的状态),如果不忙就让她过来配大官人。

看一下例子,先看下组件:

import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
import store from './store'



class TodoList extends Component {
constructor(props){
    super(props)
    //关键代码-----------start
    this.state=store.getState();
    //关键代码-----------end
    console.log(this.state)
}
    render() { 
        return ( 
            <div style={{margin:'10px'}}>
                <div>

                    <Input placeholder={this.state.inputValue} style={{ width:'250px', marginRight:'10px'}}/>
                    <Button type="primary">增加</Button>
                </div>
                <div style={{margin:'10px',width:'300px'}}>
                    <List
                        bordered
                        //关键代码-----------start
                        dataSource={this.state.list}
                        //关键代码-----------end
                        renderItem={item=>(<List.Item>{item}</List.Item>)}
                    />    
                </div>
            </div>
         );
    }
}
export default TodoList;

store代码:

import { createStore } from 'redux'  //  引入createStore方法
import reducer from './reducer'    
const store = createStore(reducer) // 创建数据存储仓库
export default store   //暴露出去

reducer:

const defaultState = {
    inputValue : 'Write Something',
    list:[
        '早上4点起床,锻炼身体',
        '中午下班游泳一小时'
    ]
}
export default (state = defaultState,action)=>{
    return state
}

这里的两个参数

  1. state: 指的是原始仓库里的状态。
  2. action: 指的是action新传递的状态。

源代码创建action并dispatch上去:

changeInputValue(e){
    const action ={
        type:'changeInput',
        value:e.target.value
    }
    store.dispatch(action)
}

在reducer里操作,写业务逻辑(Reducer里只能接收state,不能改变state

export default (state = defaultState,action)=>{
    if(action.type === 'changeInput'){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    return state
}

源代码在订阅下storechange()事件

constructor(props){
    super(props)
    this.state=store.getState();
    this.changeInputValue= this.changeInputValue.bind(this)
    //----------关键代码-----------start
    this.storeChange = this.storeChange.bind(this)  //转变this指向
    store.subscribe(this.storeChange) //订阅Redux的状态
    //----------关键代码-----------end
}

 storeChange(){
     this.setState(store.getState())
 }

添加功能与删除功能,主要在reducer里的业务代码不同:

//添加
    if(action.type === 'addItem' ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.push(newState.inputValue)  //push新的内容到列表中去
        newState.inputValue = ''
        return newState
    }
     //关键代码------------------end----------
    return state
    
 //删除
 if(action.type === 'deleteItem' ){ 
    let newState = JSON.parse(JSON.stringify(state)) 
    newState.list.splice(action.index,1)  //删除数组中对应的值
    return newState
}
 

小技巧,Actiontype单独分出一个文件,一来type多的话在大型项目里难以管理,二来在用type的时候,在源文件和reducer文件里要一一对应,错的话很难调试。定义为常量另一个好处:写错了名称,控制台会报错,大大减少找错时间,

新建actionTypes.js

export const  CHANGE_INPUT = 'changeInput'
export const  ADD_ITEM = 'addItem'
export const  DELETE_ITEM = 'deleteItem'

然后,源文件相应的名字都改为大写,替换原来的,像这样:

changeInputValue(e){
    const action ={
        type:CHANGE_INPUT,
        value:e.target.value
    }
    store.dispatch(action)
}
clickBtn(){
    const action = { type:ADD_ITEM }
    store.dispatch(action)
}
deleteItem(index){
    const action = {  type:DELETE_ITEM, index}
    store.dispatch(action)
}

对应的reducer.js文件也改为大写;

import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM} from './actionTypes'

const defaultState = {
    inputValue : 'Write Something',
    list:[
        '早上4点起床,锻炼身体',
        '中午下班游泳一小时'
    ]
}
export default (state = defaultState,action)=>{
    if(action.type === CHANGE_INPUT){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    //state值只能传递,不能使用
    if(action.type === ADD_ITEM ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.push(newState.inputValue)  //push新的内容到列表中去
        newState.inputValue = ''
        return newState
    }
    if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.splice(action.index,1)  //push新的内容到列表中去
        return newState
    }
    return state
}

三个小坑:

  • store必须是唯一的,多个store是坚决不允许,只能有一个store空间
  • 只有store能改变自己的内容,Reducer不能改变,深度拷贝验证了这一点
  • Reducer必须是纯函数,返回的结果由传入的值决定

无状态组件:组件向静态化发展,这是趋势,性能会大大提高。

  1. 首先我们不在需要引入React中的{ Component },删除就好。
  2. 然后些一个TodoListUI函数,里边只返回JSX的部分就好,这步可以复制。
  3. 函数传递一个props参数,之后修改里边的所有props,去掉this
import React from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'

const TodoListUi = (props)=>{
    return(
        <div style={{margin:'10px'}}>
            <div>
                <Input  
                    style={{ width:'250px', marginRight:'10px'}}
                    onChange={props.changeInputValue}
                    value={props.inputValue}
                />
                <Button 
                    type="primary"
                    onClick={props.clickBtn}
                >增加</Button>
            </div>
            <div style={{margin:'10px',width:'300px'}}>
                <List
                    bordered
                    dataSource={props.list}
                    renderItem={
                        (item,index)=>(
                            <List.Item onClick={()=>{props.deleteItem(index)}}>
                                {item}
                            </List.Item>
                        )
                    }
                />    
            </div>
        </div>
    )

}

export default TodoListUi;

actionCreators():每个动作在这里写,写成箭头函数

和axios的结合:在组件挂载后的生命周期函数里写入axios

componentDidMount(){
    axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{    
        const data = res.data
        const action = getListAction(data)
        store.dispatch(action)
    })
}

reducer.js:

//----关键代码--------start --------引入GET_LIST
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM,GET_LIST} from './actionTypes'
//----关键代码--------end 
const defaultState = {
    inputValue : 'Write Something',
    //----关键代码--------start --------删除原来的初始化代码,减少冗余
    list:[]
}
export default (state = defaultState,action)=>{
    if(action.type === CHANGE_INPUT){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    if(action.type === ADD_ITEM ){ 
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.push(newState.inputValue)  //push新的内容到列表中去
        newState.inputValue = ''
        return newState
    }
    if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.splice(action.index,1)  //push新的内容到列表中去
        return newState
    }
    //----关键代码--------start --------
    if(action.type === GET_LIST ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list = action.data.data.list //复制性的List数组进去
        return newState
    }
    //----关键代码--------en'd --------

    return state
}

redux-thunk:一种中间件,用它来增强dispatch。

import { createStore , applyMiddleware ,compose } from 'redux'  //  引入createStore方法
import reducer from './reducer'    
import thunk from 'redux-thunk'

const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose

const enhancer = composeEnhancers(applyMiddleware(thunk))

const store = createStore( reducer, enhancer) // 创建数据存储仓库
export default store   //暴露出去

用个增强器把thunk这个中间件加进去,当做第二个参数放进去。

有了这个中间件之后,可以把componentDidMount()的业务逻辑写在这里。

redux-saga:类似上面的内容,相当于百事可乐和可口可乐的关系,这里不展开,先用会一种再说。菜鸡心态哈哈。

react-redux和react,redux,这三者是不一样的,react-redux是react生态中的常用组件。写一个todolist.js,在构造函数里调用store

import React, { Component } from 'react';
//-----关键代码--------start
import store from './store'
//-----关键代码--------end
class TodoList extends Component {
    //-----关键代码--------start
    constructor(props){
        super(props)
        this.state = store.getState()
    }
    //-----关键代码--------end
    render() { 
        return (
            <div>
                <div>
                    //-----关键代码--------start
                    <input value={this.state.inputValue} />
                    //-----关键代码--------end
                    <button>提交</button>
                </div>
                <ul>
                    <li>JSPang</li>
                </ul>
            </div>
            );
    }
}

export default TodoList;

react-redux中的Provider和connect,提供器和连接器。provider组件里的组件都可以使用store,即用provider组件包裹。connect连接器组件暴露出去就可以使用,如下:xxx代表一个映射关系。connect的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。这也是React-Redux最大的有点。

export default connect(xxx,null)(TodoList);

而映射关系怎么做出来,就是把原来的state映射为props属性,

const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue
    }
}

把xxx改为stateToProps:

export default connect(stateToProps,null)(TodoList)

这时,put里面的state就可以改为props:

 <input value={this.props.inputValue} />

这里把全部的代码展示出来:

import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
    constructor(props){
        super(props)
        this.state = store.getState()
    }
    render() { 
        return (
            <div>
                <div>
                    <input value={this.props.inputValue} />
                    <button>提交</button>
                </div>
                <ul>
                    <li>JSPang</li>
                </ul>
            </div>
            );
    }
}

const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue
    }
}

export default connect(stateToProps,null)(TodoList);

编写DispatchToProps,这个函数是连接器的第二个参数,作用:把原来 this.inputChange.bind(this)这种双向绑定函数的方法,改为this.props方式调用方法。全部代码如下展示:

import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
    constructor(props){
        super(props)
        this.state = store.getState()
    }
    render() { 
        return (
            <div>
                <div>
                    <input value={this.props.inputValue} onChange={this.props.inputChange} />
                    <button>提交</button>
                </div>
                <ul>
                    <li>JSPang</li>
                </ul>
            </div>
            );
    }
}
const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue
    }
}

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            console.log(e.target.value)
        }
    }
}

export default connect(stateToProps,dispatchToProps)(TodoList);

映射关系做好后,还有action的派发和reducer的业务逻辑要处理:

派发action:

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            let action = {
                type:'change_input',
                value:e.target.value
            }
            dispatch(action)
        }
    }
}

reducer的业务逻辑:

const defalutState = {
    inputValue : 'jspang',
    list :[]
}
export default (state = defalutState,action) =>{
    if(action.type === 'change_input'){
        let newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    return state
}

给按钮添加事件:

1,在按钮的属性里先写方法名

<button onClick={this.props.clickButton}>提交</button>

2,在dispatchtoprops里派发action,控制台打印出111111,然后派发action

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            let action = {
                type:'change_input',
                value:e.target.value
            }
            dispatch(action)
        },
        clickButton(){
            console.log('111111111')
             let action = { type:'add_item' }
   			 dispatch(action)
            
        }
    }
}

3.编写reducer的业务逻辑,点击即可增加一个列表。

const defalutState = {
    inputValue : 'jspang',
    list :[]
}

export default (state = defalutState,action) =>{
    if(action.type === 'change_input'){
        let newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    //----关键代码------start---------
    if(action.type === 'add_item'){
        let newState = JSON.parse(JSON.stringify(state))
        newState.list.push(newState.inputValue)
        newState.inputValue = ''
        return newState
    }
    //----关键代码------end---------
    return state
}

4,在把statetoprops这个映射关系完善

const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue,
        list:state.list
    }
}

5,有了这个关系,在界面中用属性的方式调用

<ul>
    {
        this.props.list.map((item,index)=>{
            return (<li key={index}>{item}</li>)
        })
    }
</ul

这样就实现了添加列表的功能,这里展示出全部代码;

import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
    constructor(props){
        super(props)
        this.state = store.getState()
    }
    render() { 
        return (
            <div>
                <div>
                    <input value={this.props.inputValue} onChange={this.props.inputChange} />
                    <button onClick={this.props.clickButton}>提交</button>
                </div>
                <ul>
                    {
                        this.props.list.map((item,index)=>{
                            return (<li key={index}>{item}</li>)
                        })
                    }
                </ul>
            </div>
        );
    }
}
const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue,
        list:state.list
    }
}

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            let action = {
                type:'change_input',
                value:e.target.value
            }
            dispatch(action)
        },
        clickButton(){
            let action = {
                type:'add_item'
            }
            dispatch(action)
        }
    }
}
export default connect(stateToProps,dispatchToProps)(TodoList);

代码精简:


https://juejin.im/post/5e169204e51d454112714580#heading-19,这是一篇关于HOC高阶组件的文章,其中的思想也有部分精简代码的,可以学习,高级组件不是组件,而是函数,react-redux里的connect连接器就是一个高阶组件,hook可以看做对高阶组件的补充。


1,这几处的this.props都是重复的,用js的解构赋值的方式精简一下代码:

    render() { 
        let {inputValue ,inputChange,clickButton,list} = this.props;
        return (
            <div>
                <div>
                    <input value={inputValue} onChange={inputChange} />
                    <button onClick={clickButton}>提交</button>
                </div>
                <ul>
                    {
                        list.map((item,index)=>{
                            return (<li key={index}>{item}</li>)
                        })
                    }
                </ul>
            </div>
        );
    }

2,组件无状态化,就是改为一个方法,大大提高性能:

const TodoList =(props)=>{
    let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
    return (
        <div>
            <div>
                <input value={inputValue} onChange={inputChange} />
                <button onClick={clickButton}>提交</button>
            </div>
            <ul>
                {
                    list.map((item,index)=>{
                        return (<li key={index}>{item}</li>)
                    })
                }
            </ul>
        </div>
    );
}

最终的代码:

import React from 'react';
import {connect} from 'react-redux'


const TodoList =(props)=>{
    let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
    return (
        <div>
            <div>
                <input value={inputValue} onChange={inputChange} />
                <button onClick={clickButton}>提交</button>
            </div>
            <ul>
                {
                    list.map((item,index)=>{
                        return (<li key={index}>{item}</li>)
                    })
                }
            </ul>
        </div>
    );
}



const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue,
        list:state.list
    }
}

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            let action = {
                type:'change_input',
                value:e.target.value
            }
            dispatch(action)
        },
        clickButton(){
            let action = {
                type:'add_item'
            }
            dispatch(action)
        }
    }
}
export default connect(stateToProps,dispatchToProps)(TodoList);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值