react-redux 使用小结

react-redux 使用小结

好久没写 Redux 了,最近老板让写一个 POC 去把现在的 usecase+repo 的结构转成 redux,所以就……复习一下。这里的案例用的是 react-redux,原生 redux 的笔记可以参考一下这个:一文快速上手 Redux

functional based component

functional component 真的方便很多,因为不需要考虑 state 和 props 的 mapping……

store 以及 index.js 的配置也放在这里一起写了:

store:

这里用的是最新的 @reduxjs/toolkit,如果项目比较老的话,没有用最新版本的 react-redux 和 redux,就得用 import { createStore } from 'redux';configureStorecreateStore 的语法也稍有一些变化,详情可以参考官方文档。

import { configureStore } from '@reduxjs/toolkit';

const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === 'increment') {
    return { counter: state.counter + 1 };
  } else if (action.type === 'decrement') {
    return { counter: state.counter - 1 };
  }

  return state;
};

const stroe = configureStore({
  reducer: counterReducer,
});

export default stroe;

index.js:

主要用来挂载 store,也可以挂在 App level。

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';

import './index.css';
import App from './App';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

Counter:

这里实现的也就是一个 counter 的功能:

import { useDispatch, useSelector } from 'react-redux';
import classes from './Counter.module.css';

const Counter = () => {
  const dispatch = useDispatch();
  // useSelector 接受一个函数,用来返回 redux 中需要抠出来的状态
  const counter = useSelector((state) => state.counter);

  const incrementHandler = () => {
    dispatch({ type: 'increment' });
  };

  const decrementHandler = () => {
    dispatch({ type: 'decrement' });
  };

  const toggleCounterHandler = () => {};

  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
      <div className={classes.value}>{counter}</div>
      <div>
        <button onClick={incrementHandler}>increment</button>
        <button onClick={decrementHandler}>decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;

页面如下:

在这里插入图片描述

class based component

class based component 会稍微麻烦一点,每个组件都需要实现 mapStateToPropsmapDispatchToProps,并且末尾调用 connect 这个 HOC 进行组件内 state 和 props 的 mapping。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import classes from './Counter.module.css';

export class Counter extends Component {
  incrementHandler() {
    this.props.increment();
  }
  decrementHandler() {
    this.props.decrement();
  }
  toggleCounterHandler() {}
  render() {
    const { counter } = this.props;
    return (
      <main className={classes.counter}>
        <h1>Redux Counter</h1>
        <div className={classes.value}>{counter}</div>
        <div>
          <button onClick={this.incrementHandler.bind(this)}>increment</button>
          <button onClick={this.decrementHandler.bind(this)}>decrement</button>
        </div>
        <button onClick={this.toggleCounterHandler}>Toggle Counter</button>
      </main>
    );
  }
}

// connect is a HOC
const mapStateToProps = (state) => {
  return {
    counter: state.counter,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    increment: () => dispatch({ type: 'increment' }),
    decrement: () => dispatch({ type: 'decrement' }),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

我记得以前项目配置过不需要进行 mapping 来着……抽空的时候回忆一下看看能不能记一下

添加 payload

有些情况下需要在组件内部向 reducer 中传值,这里就用 functional component 来实现了。class based component 以前都是封装好了的,一下子还真的有点想不起来怎么做……

const Counter = () => {
  const increaseHandler = () => {
    dispatch({ type: 'increase', amount: 5 });
  };
};
const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === 'increase') {
    return { counter: state.counter + action.amount };
  }
};

添加多个状态

store 的修改:

import { configureStore } from '@reduxjs/toolkit';

const initialState = {
  counter: 0,
  showCounter: true,
};

const counterReducer = (state = initialState, action) => {
  if (action.type === 'increment') {
    return {
      counter: state.counter + 1,
      showCounter: state.showCounter,
    };
  } else if (action.type === 'decrement') {
    return {
      counter: state.counter - 1,
      showCounter: state.showCounter,
    };
  } else if (action.type === 'increase') {
    return {
      counter: state.counter + action.amount,
      showCounter: state.showCounter,
    };
  } else if (action.type === 'toggle') {
    return {
      showCounter: !state.showCounter,
      counter: state.counter,
    };
  }
  return state;
};

const stroe = configureStore({
  reducer: counterReducer,
});

export default stroe;

对比起来内部调用就方便很多了:

const Counter = () => {
    const showCounter = useSelector((state) => state.showCounter);

    return (
        {showCounter && <div className={classes.value}>{counter}</div>}
    )
}

redux 的状态是不可变的,因此在使用老版的操作就需要返回一个新的状态,而不能直接修改原有的状态。

redux toolkit 部分更新

找了一下资料,突然发现之前的写法已经落伍了……

但是写都写好了就懒得删除了……

所以这里对 toolkit 的配置进行一下更新。

使用 toolkit 的优点包括:

  • 可以直接对状态进行修改

    toolkit 内部会进行监听,如果察觉到状态被修改了,那么 toolkit 内部会创建一个新的状态并进行返回。也就意味着对于开发来说,就少一个状态需要进行手动管理。

  • 少些很多代码

    不需要额外设立一个变量去监听对应的 type

    reducers 直接可以归并为可调用的函数,也不用反复写很多的 switch/if-else 流程控制

store 部分代码:

import { configureStore, createSlice } from '@reduxjs/toolkit';

const initialState = {
  counter: 0,
  showCounter: true,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState: initialState,
  reducers: {
    increment(state) {
      // 可以直接进行修改
      state.counter++;
    },
    decrement(state) {
      state.counter--;
    },
    increase(state, action) {
      state.counter += action.payload;
    },
    toggle(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

const stroe = configureStore({
  reducer: counterSlice.reducer,
});

// 这样只要导出一个actions即可
// 虽然也可以说自己再封装一下,不过使用toolkit可以少写一写封装的代码也方便很多
export const counterActions = counterSlice.actions;

export default stroe;

Counter 部分代码:

import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
// 可以直接导入action并且在下面函数中调用,省了调用一些 type 的烦恼
import { counterActions } from '../store';
import classes from './Counter.module.css';

const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector((state) => state.counter);
  const showCounter = useSelector((state) => state.showCounter);

  const incrementHandler = () => {
    dispatch(counterActions.increment());
  };

  const increaseHandler = () => {
    dispatch(counterActions.increase(5)); // { type: SOME_UNIQUE_VALUE, payload: 10 }
  };

  const decrementHandler = () => {
    dispatch(counterActions.decrement());
  };

  const toggleCounterHandler = () => {
    dispatch(counterActions.toggle());
  };

  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
      {showCounter && <div className={classes.value}>{counter}</div>}
      <div>
        <button onClick={incrementHandler}>increment</button>
        <button onClick={increaseHandler}>Increase By 5</button>
        <button onClick={decrementHandler}>decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;

toolkit 管理多个状态

store 部分代码:

对于 store 来说就是创建多个 slice,并且在 configureStore 中合并创建 slices 的 reducers。

import { configureStore, createSlice } from '@reduxjs/toolkit';

const initialCounterState = {
  counter: 0,
  showCounter: true,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState: initialCounterState,
  reducers: {
    increment(state) {
      state.counter++;
    },
    decrement(state) {
      state.counter--;
    },
    increase(state, action) {
      state.counter += action.payload;
    },
    toggle(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

const initialAuthState = {
  isAuthenticated: false,
};

const authSlice = createSlice({
  name: 'auth',
  initialState: initialAuthState,
  reducers: {
    login(state) {
      state.isAuthenticated = true;
    },
    logout(state) {
      state.isAuthenticated = false;
    },
  },
});

const stroe = configureStore({
  reducer: { counter: counterSlice.reducer, auth: authSlice.reducer },
});

export const counterActions = counterSlice.actions;
export const authActions = authSlice.actions;

export default stroe;

counter:

const Counter = () => {
  const dispatch = useDispatch();
  const { counter, showCounter } = useSelector((state) => state.counter);
};

这里需要注意就是,如果不使用解构,那么就会直接获得 counter 这个 slice 中包含的状态:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值