Reputation: 175
Why does using a custom hook to render a component rerenders the App Component. If I toggle <Comp> or <AnotherComp>
the entire App component gets rerendered same for the other component.
I tried React.memo and wrap the component again in App component but has no effect.
I read that its the way react compares and treats that functions are different betwn renders. But is there an advanced pattern people use for this purpose??
export const useCard = (params)=>{
const { ToBeWrappedComponent } = params;
const [isOpen,setIsOpen] = useState(true);
const toggle= ()=> setIsOpen(!isOpen);
const WrappedComponent = ()=>{
return (
<Collapse isOpen={isOpen} >
<button onClick= {toggle}> </button>
<ToBeWrappedComponent />
</Collapse>
)
}
return [WrappedComponent,toggle]
};
const App = ()=>{
const [Comp, toggleComp] = useCard({ToBeWrappedComponent: ()=> (<h1>Wrapped Item <h1>) });
const [AnotherComp, toggleAnotherComp] = useCard({ToBeWrappedComponent: ()=> (<h1>Another Wrapped Item <h1>) })
return (
<AnotherComp > </AnotherComp>
<Comp> </Comp>
)
}
Please note this code is just an exampple I have created to demonstrate what I am facing, I do more complex things with this apporach and just want to know about Advanced patterns to achieve it and the reason for rendering. Thanks
Upvotes: 0
Views: 1327
Reputation: 2044
Because this invocation of useState
is actually called within the App component's function:
const [isOpen,setIsOpen] = useState(true);
So it becomes a part of App
's state, and not the wrapped component's state.
The component rerenders because every time the App is rerendered, the WrappedComponent
function is created anew. It is a different function with a different address in memory, and therefore the component tree is rerendered from scratch.
This is a really weird pattern you are using. You are really overcomplicating things. Why not simply pass the render function to the wrapper component?
const TogglableComponent = ({ renderWrappedComponent, isOpen, onClick }) => {
return (
<Collapse isOpen={isOpen} >
<button onClick={onClick} />
{ renderWrappedComponent() }
</Collapse>
)
};
You then control the toggle state of each component from its parent through props. Or, if you don't care about passing the toggle state to the parent component, just store it in the wrapper:
const TogglableComponent = ({ renderWrappedComponent }) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Collapse isOpen={isOpen} >
<button onClick={() => setIsOpen(!isOpen)} />
{ renderWrappedComponent() }
</Collapse>
)
};
Upvotes: 1