React renders full component instead of what changed when using wrapper function - reactjs

I observed below when rendering a list component. The list component is taking a search prop and filtering before rendering.
<UserList pattern={search}/>
when using above statement directly inside parent component, it renders only what changed based on search prop (list remains and only unmatched items got removed)
But when I wrap the UserList in another function like below:
const Users = ()=> <UserList pattern={search}/>;
and use <Users/> in parent component, I see that whole list component re-renders when search prop changes. search field is a local state (useState['']).
so, the parent component looks like this now :
const App = () => {
const [search, setSearch] = useState('');
const Users = ()=> <UserList pattern={search}/>;
return (
<div id="app" className="home-page">
<Header />
<WelcomeSlogan/>
<Users/>
</div>
);
}
So we know that react re renders full child component(s) if parent component changed, but in this case how the parent (<Users>) is changing exactly? Can someone explain to help me understand better? Thanks.

Related

Component Re-rendering issue

I am working on the project in React Typescript.
I have created hierarchy of components as per requirement.
In one scenario I have to pass data from child component to parent component and I am passing function as props and it works.
Issue :
When passing data to parent component child component gets re-render it looks like. Mean to say Dropdown selection is get reset and tree control expanded nodes get collapsed and set to the position as first time rendered.
I have used useState,useEffects hooks.
I have also tried React.memo as a part of my search on internet.
What I need :
I want to pass data to parent component from child without re-render the child component as there is no change in the props of child component.
Try this approach:
Add useCallback hook to memoize your function which lift data to <Parent />.
Then use React.memo for <Child /> to control prop changes and avoid unwanted re-renders.
I prepare an example for you here.
UPD. I have uploaded an example, you can copy it and see how it works!
Here is Child component:
const Child = ({ onChange }) => {
console.log("Child re-render");
return (
<div className="App">
<h1>Child</h1>
<button onClick={() => onChange(Math.random())}>
Lift value to Parant
</button>
</div>
);
};
const areEqual = ({ onChange: prevOnChange }, { onChange }) => {
return prevOnChange === onChange; // if true => this will avoid render
}
export default React.memo(Child, areEqual);
And the Parent:
consn App = () => {
const [value, setValue] = useState("");
const onChange = useCallback((value) => setValue(String(value)), []);
console.log("Parant re-render");
return (
<div className="App">
<h1>Parent</h1>
<div>Value is: {value}</div>
<Child onChange={onChange} />
</div>
);
}
Best regards 🚀

use react component instead of motion's elements in Framer motion

Hi I have a component like this :
const Comp = (props) => {
return(
<div>
data here
</div>
)
}
and I want to use framer motion like this:
const Container = () => {
return(
<motion.Comp variants={variants} someProp="someProp"/>
)
}
The reason I want to do this is that I don't think making a lot of div as a wrapper is a great idea.
I could use motion.div in "Comp" component but some of my app component is made that I don't need animating all of them in my whole app. I just want a solution to add animations to the first element in the component.
Also I searched and I found a solution "motion(Comp)", but it's not working.
From the docs (https://www.framer.com/docs/component/#custom-components), you can wrap any component with motion() to use it as if it were a framer-motion component. You mentioned that you found that solution but that it's not working, and without any more details about it I can't give you a specific reason why. But most likely you're forgetting to pass the ref through to your component, like this:
const Foo = React.forwardRef((props, ref) => <div ref={ref} />)
The ref is the way that framer-motion will identify which element on the page is the one it should be animating, so it needs to be passed to the correct element. Once that's done, you can wrap it with motion:
const MotionFoo = motion(Foo)
Keep in mind that regardless of how many elements your custom component has, only the one that has the ref passed to it will be animated. For example:
const BusyFoo = React.forwardRef((props, ref) => (
<div>
<div ref={ref}>I will be animated!</div>
<div>I won't be animated. :(</div>
</div>
)

React: wrap child component in div if it does not return null

Using React, how can a child component be wrapped conditionally in another element if it is not returning null when rendered?
export const Parent = ({children}) => (
<div className="row">
{React.Children.map(children, child =>
<div className="col-6 col-s-4 col-md-3">
{child}
</div>
)}
</div>
);
This might be related to the discussions in React component children detect if empty / null before render however, none of the proposed approached seemed to work (ReactDOMServer.renderToStaticMarkup seem to have issues with SSR and React.isValidElement is persistently treating the component that returns null as a valid element).
I've got a feeling that this is sort of an anti-pattern, as it seems to be real hard to do. Only solution I can think of at the moment is moving the column div into the child component and clone a prop into the child to inform it that a wrapping column div is desired... Which seems way hacky.
If you don't want to use
const isChildNull = children => {
return !Boolean(ReactDOMServer.renderToStaticMarkup(children));
};
try with:
const isChildNull = children => {
return Boolean(children.type() === null); //--> will return the tag type
};
If child component receives props, don't forget to pass those props when calling type function:
const isChildNull = children => {
const {type, props} = children;
return Boolean(type(props)=== null);
};
EDIT: working example here: https://stackblitz.com/edit/react-a29daw

React component seems to rerender because of child components (but shouldn't)

I have a page with a tabbed view. If tab 1 is active it should show component A, otherwise component B.
Based on which page/route I come from before, it should default to component B. Both the components don't need props (as they handle data/state internally).
Problem: The parent component renders three times. Because of this, I loose the state value from useLocation and can't display the second tab. Simplified parent component code:
function ContractOverview() {
const location = useLocation();
console.log(location);
const showBillingCycleTab = location.state?.selectedTab === 'billing-cycles';
return (
<Container>
<h1>
<Trans i18nKey="navigation.billing" />
</h1>
{showBillingCycleTab ? <BillingCycleTable /> : <ContractTable />} // if I do it like this, ContractOverview renders three times (and I loose location state)
{showBillingCycleTab ? <p>component A</p> : <p>component B</p>} // if I do it like this, ContractOverview renders only once and the location state is correct.
</Container>
);
}
The route is set up like this:
<Route path="/contracts" component={ContractOverview} exact />
Screenshot of the location logs:
Usually I'd expect the parent component to only render once as there are no props that could change nor component state that could do something. What am I doing wrong?

React - Calling the function of other component

I'm very very new with React. Actually I started to learn today.
I want to build a single sidebar component in a website.
I toggle a sidebar using UseState and a function called openHandler that verifies of the sidebar is already active.
I'm wondering how should I approach the toggle inside of the sidebar. How should I change the state of the sidebar if all the handle is done in the main file (App.js)
I'm really sorry if this question don't make sense.
Here is a SandBox example of what I'm talking about.
https://codesandbox.io/s/hopeful-framework-c4k1h
If someone know what should I learn to play with that would be great.
Thanks in advance
you can pass the main handler to sidebar via props and bind it to insider toggle.
appjs
...
<CartBox openHandler= {openHandler} className={ToggleCartState} />
...
cartBox.js
...
<Toggle click={props.openHandler} />
...
Nice to read https://reactjs.org/docs/lifting-state-up.html
I assume you want to be able to change the state of the sidebar by clicking a button from another sibling / child of a sibling component.
if that's the case you'll need to put the useState hook in the higher level parent, then pass the state / an it's setter method as a prop to the children that will use it.
here is an example of what I mean.
Parent Component
function parent() {
// the sidebar state
const [sidebar, setSidebar] = useState(false);
// helper function that toggles state
function toggle() {
setSidebar(!sidebar);
}
return (
<section className="Parent">
{ /* Conditional Render */
sidebar ?
<Navbar stateManager={{toggle}} />
: <HamburgerIcon stateManager={{toggle}} />
}
</section>
)
Navbar / HumburgerIcon
function Navbar({stateManager}) {
// you now passed state and it's set method to the child
const {toggle} = stateManager;
return (
<div onClick={toggle}>
component content
</div>
}
You can put them all in same file and still do the same thing.

Resources