Redux 与 React 状态管理精讲:从基础到实战

引言

Redux 是一个广泛使用的 JavaScript 状态管理库,尤其适用于 React 应用。它提供了一种可预测的方式来管理应用的状态,使得状态的变更变得可控和可追踪。本教程将从 Redux 的基本概念讲起,逐步深入到与 React 结合使用的最佳实践,以及如何处理异步操作和调试。

Redux 简介

Redux 允许你将应用的状态集中存储在一个全局的 store 中,并通过纯函数 reducer 来描述状态的变化。这种集中式的状态管理方式简化了组件间的通信,并且易于调试。

为什么使用 Redux?

  • 独立性:Redux 可以独立于框架运行,不仅限于 React。
  • 简化通信:无视组件层级,简化组件间的数据传递。
  • 数据流清晰:单向数据流使得状态变更易于追踪和定位问题。
  • 调试友好:配套的调试工具如 Redux DevTools 提供了强大的调试支持。

Redux 快速体验

实现一个计数器

不依赖任何框架或构建工具,使用纯 JavaScript 和 Redux API 来实现一个简单的计数器。

  1. 定义 reducer 函数,根据 action 返回新的状态。
  2. 使用 createStore 创建 store 实例。
  3. 使用 subscribe 订阅状态变化。
  4. 使用 dispatch 提交 action,触发状态变更。
  5. 使用 getState 获取最新状态并更新视图。
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>

<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script> 

<script>
  // 1. 定义reducer函数 
  // 作用: 根据不同的action对象,返回不同的新的state
  // state: 管理的数据初始状态
  // action: 对象 type 标记当前想要做什么样的修改
  function reducer (state = { count: 0 }, action) {
    // 数据不可变:基于原始状态生成一个新的状态
    if (action.type === 'INCREMENT') {
      return { count: state.count + 1 }
    }
    if (action.type === 'DECREMENT') {
      return { count: state.count - 1 }
    }
    return state
  }

  // 2. 使用reducer函数生成store实例
  const store = Redux.createStore(reducer)

  // 3. 通过store实例的subscribe订阅数据变化
  // 回调函数可以在每次state发生变化的时候自动执行
  store.subscribe(() => {
    console.log('state变化了', store.getState())
    document.getElementById('count').innerText = store.getState().count
  })

  // 4. 通过store实例的dispatch函数提交action更改状态 
  const inBtn = document.getElementById('increment')
  inBtn.addEventListener('click', () => {
    // 增
    store.dispatch({
      type: 'INCREMENT'
    })
  })

  const dBtn = document.getElementById('decrement')
  dBtn.addEventListener('click', () => {
    // 减
    store.dispatch({
      type: 'DECREMENT'
    })
  })

  // 5. 通过store实例的getState方法获取最新状态更新到视图中

</script>

Redux 数据流架构

Redux 的数据流向清晰,从 stateaction,再到 reducer,最后回到新的 state

Redux 与 React 环境准备

虽然 Redux 可以独立于 React 使用,但通常我们会将其与 React 结合,利用 react-redux 库来连接 Redux 和 React 组件。

配套工具

  • Redux Toolkit (RTK):官方推荐的编写 Redux 逻辑的方式,简化代码。
  • react-redux:连接 Redux 和 React 的桥梁。

配置基础环境

使用 Create React App (CRA) 创建项目,并安装必要的依赖。

npx create-react-app react-redux 
npm i @reduxjs/toolkit  react-redux 
npm run start 

Redux 与 React 实现计数器

使用 Redux Toolkit 创建 Store

利用 createSlice API 简化 slice 的创建,包括初始状态、reducers 和 actions。

//store/modules/conterStore.js
import { createSlice } from '@reduxjs/toolkit'

const counterStore = createSlice({
  // 模块名称独一无二
  name: 'counter',

  // 初始化数据state
  initialState: {
    count: 1
  },

  // 修改数据的同步方法
  reducers: {
    increment (state) {
      state.count++
    },
    decrement(state){
      state.count--
    }
  }
})

// 解构出actionCreater函数
const { increment, decrement } = counterStore.actions

// 获取reducer函数
const counterReducer = counterStore.reducer

// 以按需导出的方式导出actionCreater函数和reducer函数
export { increment, decrement }
// 以默认导出的方式导出reducer函数
export default counterReducer

为 React 注入 Store

使用 Provider 组件将 store 传递给 React 应用,建立连接。

// 项目的src/index.js中
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
  // 提供store数据
  <Provider store={store}>
    <App />
  </Provider>
)

React 组件使用 Store 数据

使用 useSelector 钩子从 store 中读取数据,使用 useDispatch 钩子分发 actions。

//app.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';

function App() {
  const count = useSelector(state => state.counter.count);
  return(
    <div className="App">
      {count}
    </div>
  );
}

export default App;

React 组件修改 Store 中的数据

使用 useDispatch 钩子生成提交 action 对象的 dispatch 函数。

//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './store/modules/counterStore';
function App() {
  
  const count = useSelector(state => state.counter.count);
  const dispatch = useDispatch();

  return(
    <div className="App">
      <button onClick={() => dispatch(increment())}>Increment</button>
      {count}
      <button onClick={() => dispatch(decrement())}>Decrement</button>
    </div>
  );
}

export default App;

Redux 与 React 提交 Action 传参

在 action 创建函数中添加参数,允许在 dispatch 时传递额外的数据。

//conterStore.js
import { createSlice } from '@reduxjs/toolkit'

const counterStore = createSlice({
  // 模块名称独一无二
  name: 'counter',

  // 初始化数据state
  initialState: {
    count: 0
  },

  // 修改数据的同步方法
  reducers: {
    increment (state) {
      state.count++
    },
    decrement(state){
      state.count--
    },
    addToNum(state, action){
      state.count += action.payload
    }
  }
})

// 解构出actionCreater函数
const { increment, decrement, addToNum } = counterStore.actions

// 获取reducer函数
const reducer = counterStore.reducer

// 以按需导出的方式导出actionCreater函数和reducer函数
export { increment, decrement, addToNum }
// 以默认导出的方式导出reducer函数
export default reducer
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';

function App() {
  const count = useSelector(state => state.counter.count);
  const dispatch = useDispatch();

  return (
    <div className="App">
      <button onClick={() => dispatch(increment())}>Increment</button>
      {count}
      <button onClick={() => dispatch(decrement())}>Decrement</button>
      <button onClick={() => dispatch(addToNum(10))}>Add 10</button>
      <button onClick={() => dispatch(addToNum(20))}>Add 20</button>
    </div>
  );
}

export default App;

Redux 异步 Action 处理

封装异步逻辑,如 API 请求,并在获取数据后 dispatch 同步 action 更新状态。

//store/modules/channelStore.js
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'

const channelStore = createSlice({
  name: 'channel',
  initialState: {
    channelList: []
  },
  reducers: {
    setChannelList (state, action) {
      state.channelList = action.payload
    }
  }
})

// 创建异步
const { setChannelList } = channelStore.actions
const url = 'http://geek.itheima.net/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {
  return async (dispatch) => {
    const res = await axios.get(url)
    dispatch(setChannelList(res.data.data.channels))
  }
}

export { fetchChannelList }

const channelReducer = channelStore.reducer
export default channelReducer


//store/index.js
import { configureStore } from '@reduxjs/toolkit'

import counterReducer from './modules/counterStore'
import channelReducer from './modules/channelStore'

 const store = configureStore({
  reducer: {
    counter: counterReducer,
    channel: channelReducer
  }
})

export default store
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';
import { fetchChannelList } from './store/modules/channelStore';
import { useEffect } from 'react';

function App() {
  const count = useSelector(state => state.counter.count);
  const {channelList} = useSelector(state => state.channel);
  const dispatch = useDispatch();
  
  //使用useEffect触发异步请求执行
  useEffect(() => {
    dispatch(fetchChannelList());
  }, [dispatch]);

  return (
    <div className="App">
      <button onClick={() => dispatch(increment())}>Increment</button>
      {count}
      <button onClick={() => dispatch(decrement())}>Decrement</button>
      <button onClick={() => dispatch(addToNum(10))}>Add 10</button>
      <button onClick={() => dispatch(addToNum(20))}>Add 20</button>
      <ul>
        {channelList.map(task => <li key={task.id}>{task.name}</li>)}
      </ul>
    </div>
  );
}

export default App;

Redux 调试 - DevTools

使用 Redux DevTools 进行状态管理和 action 的调试,提供实时的 state 展示和 action 追踪。

结语

Redux 提供了一种强大且可预测的方式来管理应用的状态。通过本教程,你已经了解了 Redux 的基本概念、如何在 React 中使用 Redux,以及如何处理异步操作和调试。希望这能帮助你更好地构建和管理复杂的前端应用状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DZSpace

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值