如何在 React Context 中实现可观察的值监视

Ngo*_*Lam 8 javascript reactive-programming typescript reactjs react-context

假设我有一个Parent组件提供Context一个Store对象。为了简单起见,我们假设这个 Store 有一个值和一个更新该值的函数

class Store {
// value

// function updateValue() {}

}

const Parent = () => {
  const [rerender, setRerender] = useState(false);
  const ctx = new Store();

  return (
    <SomeContext.Provider value={ctx}>
      <Children1 />
      <Children2 />
      .... // and alot of component here
    </SomeContext.Provider>
  );
};

const Children1 = () => {
 const ctx = useContext(SomeContext);
 return (<div>{ctx.value}</div>)
}

const Children2 = () => {
 const ctx = useContext(SomeContext);
 const onClickBtn = () => {ctx.updateValue('update')}
 return (<button onClick={onClickBtn}>Update Value </button>)
}
Run Code Online (Sandbox Code Playgroud)

所以基本上Children1会显示该值,并且在Children2组件中,有一个按钮可以更新该值。

所以我现在的问题是当Children2更新 Store 值时,Children1 不会重新渲染。来体现新的价值。

关于堆栈溢出的一种解决方案是here。这个想法是创建一个stateinParent并用它来将其传递context给孩子们。这将有助于重新渲染,Children1 因为 Parent会重新渲染。但是,我不想 Parent重新渲染,因为Parent还有很多其他组件。我只想Children1重新渲染。

那么有什么办法可以解决这个问题吗?我应该使用 RxJS 进行响应式编程还是应该更改代码中的某些内容?谢谢

Rah*_*rma 5

您可以使用像 redux lib 这样的上下文,如下所示

这很容易使用,以后如果您想转移到 redux,您只需更改存储文件,整个状态管理事物将转移到 redux 或任何其他库。

运行示例: https ://stackblitz.com/edit/reactjs-usecontext-usereducer-state-management

文章:https://rsharma0011.medium.com/state-management-with-react-hooks-and-context-api-2968a5cf5c83

减速器.js

import { combineReducers } from "./Store";

const countReducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "DECREMENT":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

export default combineReducers({ countReducer });
Run Code Online (Sandbox Code Playgroud)

商店.js

import React, { useReducer, createContext, useContext } from "react";

const initialState = {};
const Context = createContext(initialState);

const Provider = ({ children, reducers, ...rest }) => {
  const defaultState = reducers(undefined, initialState);
  if (defaultState === undefined) {
    throw new Error("reducer's should not return undefined");
  }
  const [state, dispatch] = useReducer(reducers, defaultState);
  return (
    <Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
  );
};

const combineReducers = reducers => {
  const entries = Object.entries(reducers);
  return (state = {}, action) => {
    return entries.reduce((_state, [key, reducer]) => {
      _state[key] = reducer(state[key], action);
      return _state;
    }, {});
  };
};

const Connect = (mapStateToProps, mapDispatchToProps) => {
  return WrappedComponent => {
    return props => {
      const { state, dispatch } = useContext(Context);
      let localState = { ...state };
      if (mapStateToProps) {
        localState = mapStateToProps(state);
      }
      if (mapDispatchToProps) {
        localState = { ...localState, ...mapDispatchToProps(dispatch, state) };
      }
      return (
        <WrappedComponent
          {...props}
          {...localState}
          state={state}
          dispatch={dispatch}
        />
      );
    };
  };
};

export { Context, Provider, Connect, combineReducers };
Run Code Online (Sandbox Code Playgroud)

应用程序.js

import React from "react";
import ContextStateManagement from "./ContextStateManagement";
import CounterUseReducer from "./CounterUseReducer";
import reducers from "./Reducers";
import { Provider } from "./Store";

import "./style.css";

export default function App() {
  return (
    <Provider reducers={reducers}>
      <ContextStateManagement />
    </Provider>
  );
}
Run Code Online (Sandbox Code Playgroud)

组件.js

import React from "react";
import { Connect } from "./Store";

const ContextStateManagement = props => {
  return (
    <>
      <h3>Global Context: {props.count} </h3>
      <button onClick={props.increment}>Global Increment</button>
      <br />
      <br />
      <button onClick={props.decrement}>Global Decrement</button>
    </>
  );
};

const mapStateToProps = ({ countReducer }) => {
  return {
    count: countReducer.count
  };
};

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

export default Connect(mapStateToProps, mapDispatchToProps)(
  ContextStateManagement
);
Run Code Online (Sandbox Code Playgroud)


jer*_*red 2

如果您不希望Parent组件在状态更新时重新渲染,那么您就使用了错误的状态管理模式。相反,您应该使用Redux之类的东西,它从 React 组件树中完全删除“状态”,并允许组件直接订阅状态更新。

Redux 将只允许订阅特定存储值的组件在这些值更新时更新。因此,您的父组件和分派更新操作的子组件将不会更新,而只有订阅状态的子组件会更新。效率非常高!

https://codesandbox.io/s/simple-redux-example-y3t32