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文件夹
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>
}