React之函数式组件使用Redux和react-redux

安装依赖

yarn add redux redux-thunk

在src文件夹下创建redux目录,并创建store.js和test_reducer.js

在store.js中引入redux的createStore方法和test_reducer.js

// 该文件用于暴露一个store对象,整个项目应只有一个store对象
import { createStore, applyMiddleware } from "redux"
import testData from './test_reducer'

在store配置store,并导出store

...
const store = createStore(testData)
export default store

配置reducer,reducer接受的action对象{type:'自定义类型',data:'数据'}

/* 
 * 专门为test组件创建的reducer对象
 * 接收两个参数,一个是之前的状态,一个是动作参数
*/
const initState = 0 // 初始化
function testReducer(preState = initState, action) {
    const { type, data } = action
    switch (type) {
        case ADD:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            // 项目创建时,store会自动调用一次,初始化
            return preState;
    }
}
export default testReducer

这样最精简的redux就配置好了,到组件中使用store

注:store状态更新后不会自动调用render方法,需要手动添加;函数式组件无生命周期

import React, { useRef, useState,useEffect } from 'react'
import store from '../../../redux/store'
export default function Right() {
  
  const selected = useRef(null)
  const [update,setUpdate] = useState({})
  // useEffect模拟componentDidMount()生命周期---函数式组件没有生命周期
  useEffect(() => {
    // store.subscribe()是redux提供的,监测store更新的函数
    store.subscribe(() => {
      // 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新
      setUpdate({})
    })
  })
  // 数据添加下拉框得值
  const addNumber = () => {
    store.dispatch({ type: 'add', data: Number(selected.current.value) })
    setUpdate({})
  }
  // 数据减下拉框得值
  const decrement = () => {
    store.dispatch({ type: 'decrement', data: Number(selected.current.value) })
  }
  // 如果数据是奇数,增加下拉框得值
  const incrementIfOdd = () => {
    if(store.getState() % 2 !== 0)store.dispatch({ type: 'add', data: Number(selected.current.value) })
  }
  // 异步增加
  const incrementIfAsync = () => {
    setTimeout(() => {
      store.dispatch({ type: 'add', data: Number(selected.current.value) })
    }, 1000)
  }

  return (
    <div className='page1_right_content'>
        <p>当前求和为:{store.getState()}</p>
        <select name="" id="" ref={selected}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
        </select>
        <button onClick={addNumber}>+</button>
        <button onClick={decrement}>-</button>
        <button onClick={incrementIfOdd}>是否为奇数再加</button>
        <button onClick={incrementIfAsync}>异步加</button>
    </div>
  )
}

这样最精简得redux用法就完成了...,下面我们来优化redux的使用

------------------------------------------------------------------------------------------------------------------------------

在redux目录下添加test_action.js文件,并配置对应方法

// 为test组件创建的action对象
// 加数据的方法
export const createAddAction = (data)=> ({type:'add',data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:'decrement',data})

这样我们调用reducer的时候就只需要使用action中的方法就行了

...
import { createAddAction, createDecrementAction} from '../../../redux/test_action'

...

const addNumber = () => {
    store.dispatch(createAddAction(selected.current.value*1))
  }

但是我们之前配置的action都是同步的,我们通常会遇到异步修改数据,所以还需要配置一个异步的action

// 为test组件创建的action对象
// 同步action,返回object
// 加数据的方法
export const createAddAction = (data)=> ({type:ADD,data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:DECREMENT,data})

// 异步action返回的值为函数,异步action中一般都会调用同步action
export const createAsyncAction = (data, time) => {
    // recucer调用action的时候会返回一个dispatch参数,就不需要再引入一次store了
    return dispatch => {
        setTimeout(() => {
            dispatch(createAddAction(data))
        }, time)
    }
}

使用异步action

...

const incrementIfAsync = () => {
    store.dispatch(createAsyncAction(selected.current.value*1, 1000))
  }

此时,你调用异步操作会发现程序报错了,这里我们还需要借助一个依赖redux-thunk来支持我们的异步操作

首先在store中配置异步的中间件,需要引入redux中的applyMiddleware方法

// 该文件用于暴露一个store对象,整个项目应只有一个store对象
import { createStore, applyMiddleware } from "redux"
// 引入 redux-thunk,用于支持异步操作的执行
import thunk from 'redux-thunk'

import testData from './test_reducer'
const store = createStore(testData,applyMiddleware(thunk))

export default store

这时你的程序应该能跑通了吧,不过还没完,我们还需要做进一步优化

在redux目录下再创建一个文件constant.js,用于定义常量

// 定义常量,便于管理同时防止程序员单词写错
export const ADD = 'add'
export const DECREMENT = 'decrement'

然后将使用到的字符串全部替换为常量

test_reducer.js修改特定字符串为常量(完整代码)

/* 
 * 专门为test组件(page1_right)创建的reducer对象
 * 接收两个参数,一个是之前的状态,一个是动作参数
*/
import { ADD, DECREMENT } from "./constant"
const initState = 0
function testReducer(preState = initState, action) {
    // 处于初始化状态
    // if(preState === 'undefined') preState = 0
    const { type, data } = action
    switch (type) {
        case ADD:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            // 项目创建时,store会自动调用一次,初始化
            return preState;
    }
}
export default testReducer

test_action.js修改特定字符串为常量(完整代码)

import { ADD, DECREMENT } from "./constant"
// 为test组件创建的action对象
// 同步action,返回object
// 加数据的方法
export const createAddAction = (data)=> ({type:ADD,data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:DECREMENT,data})

// 异步action返回的值为函数,异步action中一般都会调用同步action
export const createAsyncAction = (data, time) => {
    // recucer调用action的时候会返回一个dispatch参数,就不需要再引入一次store了
    return dispatch => {
        setTimeout(() => {
            dispatch(createAddAction(data))
        }, time)
    }
}

调用的完整代码

import React, { useRef, useState,useEffect } from 'react'
import store from '../../../redux/store'
import { createAddAction, createDecrementAction,createAsyncAction } from '../../../redux/test_action'
export default function Right() {
  
  const selected = useRef(null)
  const [update,setUpdate] = useState({})
  // useEffect模拟componentDidMount()生命周期---函数式组件没有生命周期
  useEffect(() => {
    // store.subscribe()是redux提供的,监测store更新的函数
    store.subscribe(() => {
      // 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新
      setUpdate({})
    })
  })
  const addNumber = () => {
    store.dispatch(createAddAction(selected.current.value*1))
  }
  const decrement = () => {
    store.dispatch(createDecrementAction(selected.current.value*1))
  }
  const incrementIfOdd = () => {
    if(store.getState() % 2 !== 0)store.dispatch(createAddAction(selected.current.value*1))
  }
  const incrementIfAsync = () => {
    store.dispatch(createAsyncAction(selected.current.value*1, 1000))
  }

  return (
    <div className='page1_right_content'>
        <p>当前求和为:{store.getState()}</p>
        <select name="" id="" ref={selected}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
        </select>
        <button onClick={addNumber}>+</button>
        <button onClick={decrement}>-</button>
        <button onClick={incrementIfOdd}>是否为奇数再加</button>
        <button onClick={incrementIfAsync}>异步加</button>
    </div>
  )
}

---------------------------------------------------------------------------------------------------------------------------------

使用react-redux(配合redux)

npm i react-redux

react-redux需要创建一个容器组件包裹UI组件,然后在容器组件中调用状态方法

在src目录下创建一个containers目录,并创建test目录及TestContainer.jsx

// 引入之前创建的Test组件---ui组件
import TestUI from '../../pages/Test'
// 引入react-redux
import { connect } from 'react-redux'

// 使用connect()(TestUI)创建并暴露一个TestUI的容器组件
export default connect()(TestUI)

同时App.jsx(或其他组件)引用Test组件就要改为引用容器组件,同时引入reudx中的store,并将其传递到容器组件

import TestContainer from './containers/test/TestContainer';
import store from './redux/store'
import './App.css';

function App() {
  return (
    <div className="App">
      <TestContainer store={store} name="test"></TestContainer>
    </div>
  );
}

export default App;

当容器组件将store接受到后,就可以进行状态的获取和修改了

react-redux的connect方法可以传入两个参数[返回状态的方法,返回操作状态的方法]

第一个参数会接收state,第二个则接收dispatch方法

// 引入ui组件
import TestUI from '../../pages/Test'
// 引入react-redux
import { connect } from 'react-redux'
import { createAddAction, createDecrementAction,createAsyncAction } from '../../redux/test_action'
// 不能直接返回数值,都返回一个对象,
function mapStateToProps(state) { // 映射状态
    return {count: state}
}
function mapDispatchToProps(dispatch) { // 映射操作状态的方法
    return {
        add: (data) => dispatch(createAddAction(data)),
        decrement: (data) => dispatch(createDecrementAction(data)),
        asyncAdd: (data,time) => dispatch(createAsyncAction(data, time))
    }
}

// 使用connect()(TestUI)创建并暴露一个TestUI的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(TestUI)

connect方法的两个参数必要以对象(函数)的方式传递,名字可自定义

然后再UI组件中可以通过props接收到传递的状态或方法

接下来就在UI组件中使用props获取和修改状态

import React, { useRef } from 'react'
export default function Test(props) {
  console.log('props', props);
  const selected = useRef(null)
  const addNumber = () => {
    props.add(selected.current.value*1)
  }
  const decrement = () => {
    props.decrement(selected.current.value*1)
  }
  const incrementIfOdd = () => {
    if (props.count % 2 !== 0) props.add(selected.current.value*1)
  }
  const incrementIfAsync = () => {
    props.asyncAdd(selected.current.value*1,1000)
  }

  return (
    <div className='page1_right_content'>
      <p>当前求和为:{props.count}</p>
      <select name="" id="" ref={selected}>
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
      </select>
      <button onClick={addNumber}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={incrementIfOdd}>是否为奇数再加</button>
      <button onClick={incrementIfAsync}>异步加</button>
    </div>
  )
}

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值