Redux 框架使用
redux 特性
单向数据流
- 用 state 来描述应用程序在特定时间点的状况
- 基于 state 来渲染出 View
- 当发生某些事情时(例如用户单击按钮),state 会根据发生的事情进行更新,生成新的 state
- 基于新的 state 重新渲染 View
不可变性 Immutability
"Mutable" 意为 "可改变的",而 "immutable 意为永不可改变。
更改内容的情况
javascript
const obj = { a: 1, b: 2 }
// 对外仍然还是那个对象,但它的内容已经变了
obj.b = 3
const arr = ['a', 'b']
// 同样的,数组的内容改变了
arr.push('c')
arr[1] = 'd'如果想要不可变的方式来更新,代码必需先复制原来的 object/array,然后更新它的复制体。
javascript
const obj = {
a: {
// 为了安全的更新 obj.a.c,需要先复制一份
c: 3,
},
b: 2,
}
const obj2 = {
// obj 的备份
...obj,
// 覆盖 a
a: {
// obj.a 的备份
...obj.a,
// 覆盖 c
c: 42,
},
}
const arr = ['a', 'b']
// 创建 arr 的备份,并把 c 拼接到最后。
const arr2 = arr.concat('c')
// 或者,可以对原来的数组创建复制体
const arr3 = arr.slice()
// 修改复制体
arr3.push('c')
redux 入门
基础实例
javascript
import { createStore } from 'redux'
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
let store = createStore(counterReducer)
store.subscribe(() => console.log(store.getState()))
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}Redux Toolkit 写法
javascript
import { createSlice, configureStore } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
incremented: (state) => {
state.value += 1
},
decremented: (state) => {
state.value -= 1
},
},
})
export const { incremented, decremented } = counterSlice.actions
const store = configureStore({
reducer: counterSlice.reducer,
})
store.subscribe(() => console.log(store.getState()))
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}redux 详细概述
state 管理
javascript
function Counter() {
// State: a counter value
const [counter, setCounter] = useState(0)
// Action: 当事件发生后,触发状态更新的代码
const increment = () => {
setCounter((prevCounter) => prevCounter + 1)
}
// View: UI 定义
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}计数器示例
index.js
javascript
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'
import * as serviceWorker from './serviceWorker'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
)app.js 包裹 Counter 组件
Counter.js
javascript
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment, incrementByAmount, incrementAsync, incrementIfOdd, selectCount } from './counterSlice'
import styles from './Counter.module.css'
export function Counter() {
const count = useSelector(selectCount)
const dispatch = useDispatch()
const [incrementAmount, setIncrementAmount] = useState('2')
const incrementValue = Number(incrementAmount) || 0
return (
<div>
<div className={styles.row}>
<button className={styles.button} aria-label="Decrement value" onClick={() => dispatch(decrement())}>
-
</button>
<span className={styles.value}>{count}</span>
<button className={styles.button} aria-label="Increment value" onClick={() => dispatch(increment())}>
+
</button>
</div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button className={styles.button} onClick={() => dispatch(incrementByAmount(incrementValue))}>
Add Amount
</button>
<button className={styles.asyncButton} onClick={() => dispatch(incrementAsync(incrementValue))}>
Add Async
</button>
<button className={styles.button} onClick={() => dispatch(incrementIfOdd(incrementValue))}>
Add If Odd
</button>
</div>
</div>
)
}counterSlice.js
javascript
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const slice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = slice.actions
export const incrementAsync = (amount) => (dispatch) => {
setTimeout(() => {
dispatch(incrementByAmount(amount))
}, 1000)
}
export const selectCount = (state) => state.counter.value
export default slice.reducerstore.js
javascript
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})