About React Memo - javascript

I am learning React.
There are several componentized buttons.
When I click on one button, the other button components, including the parent component, are also re-rendered.
import { useState } from 'react'
const Button = ({ setState, text }) => {
console.log(`${text} rendering`)
return (<button
onClick={e => setState(prevState => !prevState)}
>
{text}
</button>)
}
const Page = () => {
console.log('Page rendering')
const [aaa, setAaa] = useState(false)
const [bbb, setBbb] = useState(false)
return (<>
<div>
<Button
setState={setAaa}
text="A button"
/>
<pre>{JSON.stringify(aaa, null, 2)}</pre>
</div>
<div>
<Button
setState={setBbb}
text="B button"
/>
<pre>{JSON.stringify(bbb, null, 2)}</pre>
</div>
</>)
}
export default Page
If anyone has more information, we would appreciate it if you could enlighten us.
Thank you in advance.

Anytime you set state in a component, the component and all of its children will re-render. You could memoize the children by wrapping them in React.memo so that if their props don't change they don't re-render. However, that will increase the memory footprint of your app, so often it only makes sense to memoize components that are expensive/slow to re-render, and your Button component is fairly simple to render.

Related

I want to only a single component update on state change

I want to update only a single element when using setState in a function
import { useState } from "react";
export default function App(){
const [state, setState] = useState("foo");
return(
<Component1/>
<Component2/>
<Component3/>
);
}
I need some way of updating one some of those elements, but not all.
In functional components, you can wrap your component with React.memo. With this way, React will memorize the component structure and on next render, if the props still the same, React does not render again and use the memorized one. For more information, https://reactjs.org/docs/react-api.html#reactmemo
Basically wrap with React.memo. Below code, when state1 change, Component2's render count won't increase because its props stays same. But when state2 change, both of them will render.
export const Component2 = React.memo(({ state2 }) => {
const renderCount = useRef(0);
renderCount.current = renderCount.current + 1;
return (
<div style={{ margin: 10 }}>
Component2(with React.memo): <b>Rendered:</b> {renderCount.current} times,{" "}
<b>State2:</b> {state2}
</div>
);
});
export default function App() {
const [state1, setState1] = useState(1);
const [state2, setState2] = useState(1);
return (
<div className="App">
<div onClick={() => setState1((state) => state + 1)}>
Click to change state1
</div>
<div onClick={() => setState2((state) => state + 1)}>
Click to change state2
</div>
<Component1 state1={state1} />
<Component2 state2={state2} />
</div>
);
}
I created a sandbox for you to play. Click the buttons and see React.memo in action. https://codesandbox.io/s/xenodochial-sound-gug2x?file=/src/App.js:872-1753
Also, with Class Components, you can use PureComponents for the same purpose. https://reactjs.org/docs/react-api.html#reactpurecomponent

React rerendering list

I have a react page that has several components on it with a state that shows a modal. I dont want all the components in the app to re render when the modal shows.
const CustomersPage = () => {
const [showModal, setShowModal] = useState(false);
const dataSource = [...omitted data]
return (
<>
<Modal
visible={showModal} />
<div>
<div>
<div>
<Button type="primary" onClick={() => setShowModal(true)}>
Create
</Button>
</div>
<CustomForm />
</div>
<CustomList dataSource={dataSource} />
</div>
</>
);
};
When the showModal value changes, the components CustomForm component and the CustomList component re renders but I dont want them to re render every time the modal shows because the list can have over 100 components. How can I do this?
Edit.
const CustomList = ({ dataSource }) => {
return (
<div>
{dataSource?.map(i => (
<CustomCard
id={i.id}
...other props
/>
))}
</div>
);
};
const CustomCard = ({
... props
}) => {
return (
<>
<Card
...omitted properties
</Card>
</>
);
};
You can wrap the List and Form components in a React.memo component.
This way they will only re-render when their props change their identity.
See:
https://scotch.io/tutorials/react-166-reactmemo-for-functional-components-rendering-control
You can avoid unnecessary re-rendering with memo and hooks like useMemo and useCallback if you are using FC. Or if your are in CC the your create your component pure component that prevent unnecessary render.
Make your function component memo by wrapping component with Reaact.memo, then this will help to check and render component if there is any changes down there in your this child component. Despite all hooks like useCallback and useMemo are also helpfull for optimization.
There are tons of the articles are there about the use cases of these hooks, go and have look at them. They are really helpful.
Thanks

React render list only when data source changes

Basically I have a modal with a state in the parent component and I have a component that renders a list. When I open the modal, I dont want the list to re render every time because there can be hundreds of items in the list its too expensive. I only want the list to render when the dataSource prop changes.
I also want to try to avoid using useMemo if possible. Im thinking maybe move the modal to a different container, im not sure.
If someone can please help it would be much appreciated. Here is the link to sandbox: https://codesandbox.io/s/rerender-reactmemo-rz6ss?file=/src/App.js
Since you said you want to avoid React.memo, I think the best approach would be to move the <Modal /> component to another "module"
export default function App() {
return (
<>
<Another list={list} />
<List dataSource={list} />
</>
);
}
And inside <Another /> component you would have you <Modal />:
import React, { useState } from "react";
import { Modal } from "antd";
const Another = ({ list }) => {
const [showModal, setShowModal] = useState(false);
return (
<div>
<Modal
visible={showModal}
onCancel={() => setShowModal(false)}
onOk={() => {
list.push({ name: "drink" });
setShowModal(false);
}}
/>
<button onClick={() => setShowModal(true)}>Show Modal</button>
</div>
)
}
export default Another
Now the list don't rerender when you open the Modal
You can use React.memo, for more information about it please check reactmemo
const List = React.memo(({ dataSource, loading }) => {
console.log("render list");
return (
<div>
{dataSource.map((i) => {
return <div>{i.name}</div>;
})}
</div>
);
});
sandbox here

React - Change state in child component from another child component

I need to change the state in one child component when a button is clicked in another child component. Both the childs have the same parent component.
import React from "react":
import A from "...";
import B from "...";
class App extends React.Component {
render() {
<div>
<A />
<B />
</div>
}
}
In this example, when a button in component A is pressed, the state in component B needs to be changed.
This application sounds like the perfect use case for "Lifting State Up", i.e. holding the main state in the parent component. Then you basically just pass down handlers (to change the parent state) to component A (this becomes the button's onClick handler), then pass down the state you want to show to component B.
When you click the button, the setState is called in the parent component, which automatically re-renders all children whose props change (including component B).
Here's more detailed info: https://reactjs.org/docs/lifting-state-up.html
EDIT: The reply below reminded me that I should probably add some code to illustrate - but I've made a few changes that simplify things.
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false);
function handleToggle() {
// Decouple the implementation of the parent state change from the child
// Pass a function to change the state (async/batching reasons)
setShow(show => !show);
}
return (
<div>
<A show={show} onToggle={handleToggle} />
<B show={show} onToggle={handleToggle} />
</div>
)
}
const A = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
const B = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
So basically we don't care how the state in the parent is changed. We just know that when the button in the child component is clicked, we want to trigger that change. So all we really have to do is call the function passed down via props - we don't have to pass any params.
The parent will then handle any clicks inside the handleToggle function, and you can change this implementation in the future without the child knowing anything. Perhaps you want to change to use mobx to handle state, or run some other code before finally changing the state. Since both are decoupled, you're all good! I've also adjusted setShow to use a function (benefits described here: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).
A supplement to the answer above:
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false)
return (
<div>
<A show={show} setShow={setShow} />
<B show={show} setShow={setShow} />
</div>
)
}
const A = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)
const B = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)

How to give React parent component access to children state without form (with Sandbox)?

I'm currently looking for a way to access children state from a parent component that will handle API calls for the whole page.
The actual problem is the following:
Parent is the parent component that will render two Child components.
Each of the Child has a state that it is responsible for.
The "Kind of Submit Button" will have a "Kind of Submmit Action" (this is all quoted because this is not a form) and that should fire the function to provide access to the children state. Is there a way (some React feature) to do this without using <form> or without creating an intermediate parent component to hold all the state? I want each children to be responsible for its own state.
Code Sandbox with example of the code below
import React, { useState, useRef } from "react";
function ChildOne() {
const [childOneState, setChildOneState] = useState(false);
return (
<React.Fragment>
<h3>Child One</h3>
<p>My state is: {childOneState.toString()}</p>
<button onClick={() => setChildOneState(true)}>Change my state</button>
</React.Fragment>
);
}
function ChildTwo() {
const [childTwoState, setChildTwoState] = useState(false);
return (
<React.Fragment>
<h3>Child Two</h3>
<p>My state is: {childTwoState.toString()}</p>
<button onClick={() => setChildTwoState(true)}>Change my state</button>
</React.Fragment>
);
}
function Button(props) {
return (
<button onClick={props.kindOfSubmitAction}>Kind of Submit Button</button>
);
}
function Parent() {
const childOneState = useRef("i have no idea");
const childTwoState = useRef("ihave no idea");
function kindOfSubmitAction() {
console.log("This is the kindOfSubmit function!");
// This function would somehow get
// access to the children state and store them into the refs
return;
}
return (
<React.Fragment>
<h1>Iam Parent</h1>
<div>
<b>Child one state is: </b>
{childOneState.current}
</div>
<div>
<b>Child two state is: </b>
{childTwoState.current}{" "}
</div>
<Button kindOfSubmitAction={kindOfSubmitAction} />
<ChildOne />
<ChildTwo />
</React.Fragment>
);
}
export default Parent;
When several components need access to the same data, it's time for Lifting State Up.

Categories

Resources