react状态管理

useContext

语法:

somecontext = createContext(defaultValue);

作用: 避免了组件嵌套太深, 顶层变量层层传递的麻烦.

第一步: 用createContext声明一个context上下文变量

import { createContext } from 'react';
export const GlobalContext = createContext({});
export const GlobalContextProvider = GlobalContext.Provider;
export default GlobalContext;

● 参数defaultValue: 如果在组件的顶层树没有匹配到context Provider,就会使用defaultValue,它是静态的并且永远不会被改变. 如果你没有什么特别想声明的默认值, 写个null就行.
● 返回值: 返回一个context对象, 它本身不带有任何信息,它代表了其他组件可以提供和读取的上下文.生成的context对象有两个属性,分别是:
○ context.Provider: 让你用来给组件们提供context变量
○ context.Consumer: 另一种比较少用的, 用来消费context变量的方法( useContext出现之前的一种老的读取context的方法, 不推荐使用, 现在推荐用useContext )

第二步: 用context.Provider给下层组件传递要消费的值
像下面这样, 将你的子组件用context.Provider包裹, 并通过value向内部所有的组件们提供可用的值,

import ComponentB from './ComponentB';

const Benson = () => {
  const [value, setValue] = useState(0)
  return (
    <GlobalContextProvider value={{ value}}>
      {JSON.stringify(value)}
      <ComponentB />
    </GlobalContextProvider>
  )
}
export default Benson

第三步: 用useContext消费顶层树提供的数据
所有子组件就可以通过调用usecontext(ThemeContext)去读取这个context的变量,无论这些子组件嵌套有多深

import GlobalContext from './Contexts';

const ComponentB = () => {
    const { value } = useContext(GlobalContext)
    return <div>Component B</div>;
};

export default ComponentB;

只要来自父组件的context改变了, react就会重新渲染和更新ui

如果想在子组件更新这些数据怎么办?

总不能从顶层传递一个setstate下来吧,这多麻烦啊, 特别是如果你的项目中组件嵌套很深的情况, 有个简单的方法,就是从context.Provider的value中 pass down 一个 dispatch 函数 from useReducer via context

import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
const Benson = () => {
  const [value, dispatch] = useReducer((pre, action) => {
    return { ...pre, ...action }
  }, { a: 1, b: 2 })

  return (
    <GlobalContextProvider value={{ value, dispatch }}>
      {JSON.stringify(value)}
      <ComponentB />
      <ComponentA />
    </GlobalContextProvider>
  )
}
export default Benson

这样TodosApp中所有的子组件就可以使用dispatch去更新顶层数据了

import GlobalContext from './Contexts';
const ComponentB = () => {
    const { value, dispatch } = useContext(GlobalContext)
    const handelDispatch = () => {
        dispatch({ c: 3 })
    }
    return <div onClick={handelDispatch}>Component B</div>;
};

export default ComponentB;

问题: ComponentB中dispatch一个aciton,是否会导致用memeo包裹的ComponentA重新渲染

const ComponentA = memo(({ data }) => {
    // 自定义比较函数
    console.log('重新渲染ComponentA组件');
    return <div >Component A</div>;
});

export default ComponentA;

答案: 不会

redux

1. 安装 Redux

npm install redux react-redux

2. 创建 Redux Store

Redux store 是存储应用状态的地方。你需要定义一个 reducer 来描述如何根据动作更新状态。

import { createStore } from 'redux';

// 定义初始状态
const initialState = {
  count: 0
};

// 定义 reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};


3. 提供 Redux Store 给 React 应用

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';  // 假设 App 是你的根组件

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

4. 连接 React 组件和 Redux

import React from 'react';
import { connect } from 'react-redux';

// 定义 React 组件
const Counter = ({ count, increment, decrement }) => (
  <div>
    <h1>{count}</h1>
    <button onClick={increment}>Increment</button>
    <button onClick={decrement}>Decrement</button>
  </div>
);

// 定义 mapStateToProps 函数
const mapStateToProps = state => ({
  count: state.count
});

// 定义 mapDispatchToProps 函数
const mapDispatchToProps = dispatch => ({
  increment: () => dispatch({ type: 'INCREMENT' }),
  decrement: () => dispatch({ type: 'DECREMENT' })
});

// 使用 connect 将组件连接到 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

react-redux 的核心组件只有两个,Provider 和 connect。

Provider

Provider 其实就只是一个外层容器,它的作用就是通过配合 connect 来达到跨层级传递数据。使用时只需将Provider定义为整个项目最外层的组件,并设置好store。那么整个项目都可以直接获取这个store。它的原理其实是通过React中的Context来实现的。

connect

connect 的作用是连接React组件与 Redux store,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

它共有四个参数mapStateToProps, mapDispatchToProps, mergeProps以及options。

mapStateToProps 的作用是将store里的state(数据源)绑定到指定组件的props中
mapDispatchToProps 的作用是将store里的action(操作数据的方法)绑定到指定组件的props中

在现代的 React 开发中,使用 React Redux 提供的 connect 函数已经逐渐被 React Redux 的 useSelector 和 useDispatch hooks 所取代。这些 hooks 提供了一种更加简洁和直观的方式来访问 Redux store,并且更符合函数组件的编写习惯。

利用useSelector/useDispatch的写法

counterSlice.js. //用来定义reducer

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

export const counterSlice = createSlice({
    name: 'count',
    initialState: {
        value: 1,
    },
    reducers: {
        increment: (state) => {
            state.value += 1;
        },
        decrement: (state) => {
            state.value -= 1;
        },
        incrementByAmount: (state, action) => {
            state.value += action.payload;
        },
    },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

export default counterSlice.reducer;

rootReducer.js(将上面定义的reducer引入rootReducer中)

import { combineReducers } from '@reduxjs/toolkit';
import counterSlice from './counterSlice';

const rootReducer = combineReducers({
    counter: counterSlice, 
    a: aSlice //可以设置多个reduce
   
});

export default rootReducer;


3. 利用configureStore创建一个store

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

const store = configureStore({
    reducer: rootReducer,
    
});

export default store;

//Counter.js(消费这个store)

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from '../counterSlice';

function Counter() {
    const count = useSelector((state) => { console.log(state); return state.counter.value });
    const dispatch = useDispatch();

    return (
        <div>
            <h1>{count}</h1>
            <button onClick={() => dispatch(increment())}>Increment</button>
            <button onClick={() => dispatch(decrement())}>Decrement</button>
            <button onClick={() => dispatch(incrementByAmount(2))}>Increment by 2</button>
        </div>
    );
}

export default Counter;

区别

  1. 依赖和复杂度:

useContext 和 useReducer 是 React 内置的,不需要额外的依赖。
Redux 是一个独立的库,需要额外安装和配置。
数据流和架构:

useContext 和 useReducer 的数据流相对简单,适合中小型应用。
Redux 提供更强大的中间件支持和开发工具,适合大型复杂应用。

  1. 开发工具:

Redux 拥有丰富的开发工具(如 Redux DevTools),便于调试和状态管理。
useContext 和 useReducer 调试相对不那么方便,但可以借助 React DevTools。
中间件和扩展性:

Redux 支持中间件(如 redux-thunk、redux-saga)来处理异步操作和副作用。
useContext 和 useReducer 需要自定义解决异步问题,缺乏内置的中间件机制。

  1. 全局状态管理:

Redux 更加适合需要全局一致性和复杂状态管理的应用。
useContext 和 useReducer 更适合局部或中小型状态管理。

参考自文档: https://legacy.reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值