Xitroff
Xitroff

Reputation: 121

React wrapper component (HOC) does not re-render child component when it's props are changing

My wrapper component has this signature

const withReplacement = <P extends object>(Component: React.ComponentType<P>) =>
  (props: P & WithReplacementProps) => {...}

Btw, full example is here https://codepen.io/xitroff/pen/BaKQNed

It's getting original content from argument component's props

interface WithReplacementProps {
  getContent(): string;
}

and then call setContent function on button click.

const { getContent, ...rest } = props;
const [ content, setContent ] = useState(getContent());

I expect that content will be replaced everywhere (1st and 2nd section below). Here's the part of render function

return (
      <>
        <div>
          <h4>content from child</h4>
          <Component
            content={content}
            ReplaceButton={ReplaceButton}
            {...rest as P}
          />
          <hr/>
        </div>

        <div>
          <h4>content from wrapper</h4>
          <Hello
            content={content}
            ReplaceButton={ReplaceButton}
          />
          <hr/>
        </div>
      </>
    );

Hello component is straightforward

<div>
  <p>{content}</p>
  <div>
    {ReplaceButton}
  </div>
</div>

and that's how wrapped is being made

const HelloWithReplacement = withReplacement(Hello);

But the problem is that content is being replaced only in 2nd part. 1st remains untouched.

In the main App component I also replace the content after 20 sec from loading.

const [ content, setContent ] = useState( 'original content');

  useEffect(() => {
    setTimeout(() => {
      setContent('...too late! replaced from main component');
    }, 10000);
  }, []);

...when I call my wrapped component like this

return (
    <div className="App">
      <HelloWithReplacement
        content={content}
        getContent={() => content}
      />
    </div>
  );

And it also has the issue - 1st part is updating, 2nd part does not.

Upvotes: 0

Views: 793

Answers (1)

Julian Torregrosa
Julian Torregrosa

Reputation: 861

It looks like you are overriding the withReplacement internal state with the external state of the App

<HelloWithReplacement
    content={content} // Remove this to stop overriding it
    getContent={() => content}
  />

Anyway it looks weird to use two different states, it is better to manage your app state in only one place

Upvotes: 1

Related Questions