Реагируйте, предотвращайте повторную визуализацию дочернего компонента при использовании контекстного API

#reactjs

Вопрос:

Я знаю, что это хорошо известная проблема, и есть так много статей, вопросов и ответов. Но у меня все еще есть нерешенная проблема (возможно, я не смог ее найти).

Во-первых, вот код.

Контекст

 import React, { FC, createContext, useContext, useState } from 'react';

// Context
const useCounterAndContent = () => {
  const [count, setCount] = useState(0);
  const [content, setContent] = useState('');

  const increase = () => setCount(count   1);

  return {
    count,
    increase,

    content,
    setContent
  };
};

const CounterAndContentContext = createContext<null | ReturnType<typeof useCounterAndContent>>(null);

const CounterAndContentContextProvider: FC = ({ children }) => {
  const counter = useCounterAndContent();

  <CounterAndContentContext.Provider value={counter}>
    {children}
  </CounterAndContentContext.Provider>
};
 

Компоненты

 // wrapper for checking re-render
const Floor: FC<{ name: string; }> = ({ children, name }) => {
  console.log(`render floor of ${name}`);
  return <div>{children}</div>;
};

const Counter: FC = () => {
  const counterAndContent = useContext(CounterAndContentContext);

  console.log('render Counter');

  return (
    <>
      <span>{counterAndContent?.count}</span>
      <button type="button" onClick={() => counterAndContent?.increase()}>
        Increase
      </button>
    </>
  );
};

const InputContent = () => {
  const counterAndContent = useContext(CounterAndContentContext);

  console.log('render InputContent');

  return (
    <input
      type="text"
      value={counterAndContent?.content}
      onChange={({ target: { value } }) => counterAndContent?.setContent(value)}
    />
  );
};
 

And here is the main issue.

1

 const Main = () => {
  return (
    <CounterAndContentContextProvider>
      <Floor name="counter">
        <Counter />
      </Floor>
    </CounterAndContentContextProvider>
  );
};
 

When I make an app like this, and click the Increase button(Counter Component) two times, the console shows

 render floor of counter -> when render
render Counter -> when render
render Counter -> when click
render Counter -> when click
 

2

 const Main = () => {
  return (
    <CounterAndContentContextProvider>
      <Floor name="counter">
        <Counter />
      </Floor>
      <Floor name="content">
        <InputContent />
      </Floor>
    </CounterAndContentContextProvider>
  );
};
 

When I make an app like this, and click the Increase button(Counter Component) two times, the console shows. It also happen whenever change the input value.

 render floor of counter -> when render
render Counter -> when render
render floor of content -> when render
render InputContent -> when render

render Counter -> when click
render InputContent -> when click

render Counter -> when click
render InputContent -> when click
 

The Floor component is only rendered when the App is rendered. But I think the InputContent component and Counter component is not related to each other, i.e. when I click the Increase button, React have to render the Counter component only not InputContent component.
How can I achieve it? (when I click the Increase button, the Counter component is only rendered)

plus

I make the context CounterAndContentContext like following

 const CounterAndContentContext = createContext<null | ReturnType<typeof useCounterAndContent>>(null);
 

и используйте его в компоненте, например (используя необязательную цепочку)

 ({ target: { value } }) => counterAndContent?.setContent(value)
 

Есть ли какой-нибудь способ использовать это как ?

 const CounterAndContentContext = createContext<ReturnType<typeof useCounterAndContent>>(proper value);

// remove optional chaining
({ target: { value } }) => counterAndContent.setContent(value)