且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

React Native 防止上下文依赖重新渲染所有组件

更新时间:2023-12-02 20:42:40

原来我必须将 Context 拆分为两个,一个没有依赖关系,只运行函数,另一个显示任何值) 实时通过添加依赖项.与消费者组件相同,将其作为新组件取出并放入useContext,这样父组件就不会被重新渲染.

I have three buttons. When I click one button, it fires Context's memo function to add an item into Context's items. I see that clicking one button re-renders all the three buttons, which is not expected. I need to re-render only the clicked button. Please advice.

CounterContext.js

import { createContext } from "react";

export const CounterContext = createContext(null);

App.js

import React from "react";
import { Text, StyleSheet } from "react-native";
import MyButton from "./MyButton";
import { CounterContext } from "./CounterContext";

function counterReducer(prevState, action) {
  switch (action.type) {
    case "ADD_ITEM":
      let items = [...prevState.items, action.item];

      return {
        items: items,
        count: items.length
      };
    default:
      console.log("No action type");
      break;
  }
}
const buttons = ["1", "2", "3"];

export default function App() {
  const [counterState, counterDispatch] = React.useReducer(counterReducer, {
    items: [],
    count: 0
  });

  const counterContext = React.useMemo(
    () => ({
      addItem: item => {
        counterDispatch({
          type: "ADD_ITEM",
          item: item
        });
      },
      items: counterState.items,
      itemsCount: counterState.items.length
    }),
    [counterState.items]
  );

  return (
    <CounterContext.Provider value={counterContext}>
      {buttons.map((button, index) => {
        return <MyButton key={index} id={button} />;
      })}
      <Text style={styles.counter}>{counterContext.itemsCount}</Text>
      <Text style={styles.items}>{JSON.stringify(counterContext.items)}</Text>
    </CounterContext.Provider>
  );
}

const styles = StyleSheet.create({
  counter: {
    margin: 10,
    fontSize: 30
  },
  items: {
    margin: 10,
    fontSize: 18
  }
});

MyButton.js

import React from "react";
import { TouchableOpacity, Text, StyleSheet } from "react-native";
import { CounterContext } from "./CounterContext";

export default function MyButton(props) {
  const { addItem } = React.useContext(CounterContext);

  let time = new Date().toLocaleTimeString();

  console.log(time + " - Rendering MyButton #" + props.id + " ...");

  return (
    <TouchableOpacity
      style={styles.myButton}
      onPress={() => {
        addItem({
          name: "Item #" + props.id
        });
      }}
    >
      <Text>Add</Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  myButton: {
    width: 100,
    margin: 10,
    padding: 10,
    textAlign: "center",
    borderWidth: 1,
    borderColor: "#ccc",
    backgroundColor: "#e0e0e0",
    borderRadius: 5
  }
});

Here is the Codesandbox codes: https://codesandbox.io/s/usecontext-dependency-fpcep?file=/src/App.js

Turns out that I have to split Context into two, one that doesn’t have dependency, only to run the functions, and one that shows any value(s) real-time by adding the dependency. The same with the consumer component(s), take it out as new component and put the useContext in there, so parent component won’t be re-rendered.