react redux使用

前置知识

发布者订阅者模式

可用于非父子组件传值。
1.创建一个bus.js 用来放订阅者和发布者的方法。

let bus = {
  arr: [],
  // 订阅者(传入回调
  subScribe(callback) {
    this.arr.push(callback)
  },
  // 发布者(执行回调,传入参数
  publish(params) {
    this.arr.forEach(callback => {
      callback && callback(params)
    })
  }

}

export default bus

组件A

import React from 'react'
import bus from '../../utiles/bus'

export default function Add() {
  return (
    <div>

      <button onClick={() => {
        bus.subScribe((val) => {
          console.log(val)
        })
      }}>加</button>
    </div>
  )
}

组件B

import React from 'react'
import bus from '../../utiles/bus'

export default function Jian() {
  return (
    <div>
      <button onClick={() => {
        bus.publish(114514)
      }}>减</button>
    </div>
  )
}

useContext

用于向子组件传递数据,方法。配合useReducer,管理状态
什么是上下文呢?
全局变量就是全局的上下文,全局都可以访问到它;上下文就是你运行一段代码,所要知道的所有变量
使用
1.要先创建createContex
使用createContext创建并初始化
const c = React.createContext(null) // 写在组件外面

2.Provider 指定使用的范围
在圈定的范围内,传入读操作和写操作对象,然后可以使用上下文

 <C.Provider value={{n,setN}}>
      这是爷爷
      <Baba></Baba>
    </C.Provider>

3.最后使用useContext
使用useContext接受上下文,因为传入的是对象,则接受的也应该是对象

const {n,setN} = useContext(C);

案例
注意,不在一个文件中要导出,导入c

import React, { createContext, useContext, useReducer, useState } from 'react'
import ReactDOM from 'react-dom'

// 创造一个上下文
const C = createContext(null);

function App(){
  const [n,setN] = useState(0)
  return(
    // 指定上下文使用范围,使用provider,并传入读数据和写入据
    <C.Provider value={{n,setN}}>
      这是爷爷
      <Baba></Baba>
    </C.Provider>
  )
}

function Baba(){
  return(
    <div>
      这是爸爸
      <Child></Child>
    </div>
  )
}
function Child(){
  // 使用上下文,因为传入的是对象,则接受也应该是对象
  const {n,setN} = useContext(C)
  const add=()=>{
    setN(n=>n+1)
  };
  return(
    <div>
      这是儿子:n:{n}
      <button onClick={add}>+1</button>
    </div>
  )
}


ReactDOM.render(<App />,document.getElementById('root'));

useReducer

作用:配合useContext做复杂的父子通信.
在 hooks 中提供了的 useReducer 功能,可以增强 ReducerDemo 函数提供类似 Redux 的功能,引入 useReducer 后,useReducer 接受一个 reducer 函数作为参数,reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath,count 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的。

count.js

import React, { useReducer } from 'react'
import Add from '../../components/reduxStudy/Add'
import Jian from '../../components/reduxStudy/Jian'


// 处理函数
const reducer = (prevState, action) => {
  let newState = { ...prevState }
  if (action.type == 'add') {
    newState.count += 1
    return newState
  } else {
    newState.count -= 1
    return newState
  }
}
// 状态
const initState = {
  count: 1
}

const TestContext = React.createContext({})

export default function Count() {
  const [state, dispatch] = useReducer(reducer, initState)
  return (
    <TestContext.Provider value={{
      state,
      dispatch
    }}>
      <div>
        <Jian></Jian>
        <Add></Add>
      </div>
    </TestContext.Provider>
  )
}

export {
  TestContext
}

add.js

import React, { useContext } from 'react'
import { TestContext } from '../../views/reduxstudy/Count'



export default function Add() {
  const { state, dispatch } = useContext(TestContext)
  return (
    <div>
      <p>{state.count}</p>
      <button onClick={() => {
        dispatch({
          type: 'add'
        })
      }}>加</button>
    </div>
  )
}

redux基本使用

安装redux依赖 npm i redux
新建store.js文件夹
image

index.js

import { createStore, combineReducers } from 'redux'
import cityReducer from './modules/City'
import animalReducer from './modules/Animal'

// reducer合并(模块化
//当开发中reducer都放现在一个文件时,多人开发代码会冲突,所以我们可以有多个reducer然后合并。
const reducer = combineReducers({
  cityReducer,
  animalReducer
})


const store = createStore(reducer)

export { store }

modules/Animal

const animalReducer = (prevState = { uname: 'cat' }, action) => {
  let newState = { ...prevState }
  if (action.type == 'change') {
    newState.uname = 'dog'
    return newState
  } else if (action.type == 'change2') {
    newState.uname = 'birds'
    return newState
  }
  else {
    return newState
  }
}

export default animalReducer

action/Animal

class AnimaAction {
  changeAnimal() {
    return {
      type: 'change',
      paload: 12
    }
  }
}

export default new AnimaAction

在页面中使用

import React, { useState } from 'react'
import { store } from '../../store/index.js'
import animaAction from '../../store/actions/animaAction.js'

export default function Animal() {
  return (
    <div>
      <p>{data}</p>
      <button onClick={() => {
        // 发布者
        store.dispatch(animaAction.changeAnimal())
      }}>点击</button>
    </div>
  )
}

订阅者

import React, { useState } from 'react'
import { store } from '../../store/index.js'
import animaAction from '../../store/actions/animaAction.js'

export default function Animal() {
  const [data, setData] = useState('cat')
  // 订阅者,一旦数据改变就会被触发执行
  store.subscribe(() => {
    setData(() => {
      return store.getState().animalReducer.uname
    })
  })
  return (
    <div>
      <p>{data}</p>
      <button onClick={() => {
        // 发布者
        store.dispatch(animaAction.changeAnimal())
      }}>点击</button>

    </div>
  )
}

redux-thunk

当有异步任务的时候,我们需要异步去更改store的状态,这时我们可以使用redux-thunk.
index.js

import { createStore, applyMiddleware, combineReducers } from 'redux'
import reduxThunk from 'redux-thunk'  // 引入 redux-thunk
import cityReducer from './modules/City'
import animalReducer from './modules/Animal'


const reducer = combineReducers({
  cityReducer,
  animalReducer
})

// 使用中间件
const store = createStore(reducer, applyMiddleware(reduxThunk))

export { store }

animaAction.js

class AnimaAction {
  // 异步
  // redux-thunk配置好之后,dispatch可以传递一个函数了
  // 这个函数接收一个dispatch函数,调用dispatch的时候就可以更新状态了

  turnBirds() {
    return (dispatch) => {
      setTimeout(() => {
        dispatch({
          type: 'change2',
          paload: 12
        })
      }, 1000);
    }
  }
}

export default new AnimaAction

使用

import React, { useState } from 'react'
import { store } from '../../store/index.js'
import animaAction from '../../store/actions/animaAction.js'

export default function Animal() {
  const [data, setData] = useState('cat')
  // 订阅者,一旦数据改变就会被触发执行
  store.subscribe(() => {
    setData(() => {
      return store.getState().animalReducer.uname
    })
  })
  return (
    <div>
      <p>{data}</p>
      <button onClick={() => {
        store.dispatch(animaAction.turnBirds())
      }}>异步</button>
    </div>
  )
}

redux-promise

和 redux-thunk一样,只不过传入的是一个promise对象

import store from '../store/index'
export default function(){
 
  function cahngeCity(){
    store.dispatch(new Promise((result,reject)=>{
      setTimeout(()=>{
        result()
      },1000)
    }).then(()=>{
      return {
        type:'change-city',
        data:'天津'
      }
    }))
  }
  return <div>
    天津<button onClick={ ()=>{ cahngeCity() } }>切换天津</button>
  </div> 
}

取消订阅

注意:一定主要在组件函数中订阅,因为你取消订阅时,会导致组件重新渲染,重新调用组件函数,调用组件函数时又会订阅上消息,无限循环.....

import store from '../store/index'
import { useState } from 'react'
 
export default function(){
  const [city,changeCity] =  useState()
  const [unsubscribe,changeUnsubscribe] = useState()
 
  function subscribe(){
    console.log('订阅')
    // 订阅时,会返回一个唯一函数,调用这个函数就会取消订阅
    const unsubscribeCall = store.subscribe(()=>{
      changeCity(store.getState().city)
    })
 
    changeUnsubscribe({unsubscribe:unsubscribeCall})
  }
 
  function cancelSubscribe(){
    unsubscribe.unsubscribe()
    console.log('取消订阅')
  }
 
  return <div>
    <input type="text" defaultValue={ city }/>
    <button onClick={ ()=>{ subscribe() } }>订阅</button>
    <button onClick={ ()=>{ cancelSubscribe() } }>取消订阅</button>
  </div>
}
posted @ 2022-11-03 16:11  yunChuans  阅读(17)  评论(0编辑  收藏  举报