setState in useEffect with that state as dependency - javascript

I have A parent component with a child modal component. the parent component has a blank state variable passed down to child. the parent component also has a div that needs editorState initialized on render to work properly. My child component has a button that sets stateForDiv with some data to render in parent components div. I need parent component to setEditorState when child updates stateForDiv. Naturally I tried to use useEffect with stateForDiv as dependency. Well when the useEffect runs it creates and infinite loop when I run setEditorState. How can I set EditorState every time the stateForDiv changes without creating an infinite loop?
import React, {useState, useEffect} from 'react';
import {convertFromRaw, EditorState } from 'draft-js';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const [ editorState, setEditorState ] = useState(() => { return EditorState.createEmpty()}) // this must be initialized once on render
const [ stateForDiv, setStateForDiv ] = useState() // this state is passed down to child component, when the child component updates this state, we need to setEditorState.. this is my problem, infinite loop in useEffect when using stateForDiv as dependency
useEffect(() => {
if(stateForDiv) {
setEditorState(EditorState.createWithContent(
convertFromRaw(stateForDiv)));
} else {
console.log("false")
}
}, [stateForDiv])
return (
<div currentContent={editorState} onChange={setEditorState} >
</div>
<div>
<Modal>
<ChildComponent setStateForDiv={setStateForDiv}
</Modal>
</div>
Child component that is setting state for stateForDiv
const ChildComponent = ({setStateForDiv}) => {
return (
<div>
<button onClick={() => {setStateForDiv("<p>hi there</p>")} } ></button>
</div>
)
}

First point, const [ stateForDiv, setStateForDiv ] = useState() this statement is not required. Beacause you just want <ChildComponent /> component which onClick event to control <div currentContent={editorState} > component display. You can <ChildComponent onClick={setEditorState(EditorState.createWithContent(convertFromRaw("<p>hi there</p>")));}/>.
In addition, why your useEffect infinite loop.As your useEffect dependence is wrong.So you should take care how to use dependence.

Related

Is possible to not mount a component everytime is rendered by a father component?

In Father component I render Child component that is a form with states inside. When I render Father i don't want to mount Child component and reset it's states. It is possible?
I tried rendering Child component using useMemo hook, but it mounts the component everytime the hook returns it.
const Father = () => {
const [formData, setFormData] = useState(null);
const renderForm = useMemo(() => {
return (
<Child
setDefaultFormData={(value) => setFormData(value)}
>
</Child>
);
}, [formData]);
return (
<>
<div>{renderForm}</div>
</>
);
};
export default Father;
Any help out there? Thanks.
I'm not quite sure why you added formData to the useMemo dependency list.
You are updating formData based on the previous state, and formData is not needed in the dependency list.
Try this:
const renderForm = useMemo(() => {
return (
<Child
setDefaultFormData={(value) => setFormData(value)}
>
</Child>
);
}, []);
useMemo works as expected in this case, it only updates when some dependencies in the dependency list have been updated.
This part of code setDefaultFormData={(value) => setFormData(value)} leads to the creation of a new function every time the parent renders which causes the Child component to re-render because prevProps.setDefaultFormData != nextProps.setDefaultFormData, beside that React by default re-renders all children when parent component renders, so you have to wrap your child component with React.memo() and pass setFormData directly setDefaultFormData={setFormData} and use it normally

Component should return same value wherever it's called. and Main Component should not rerender when Sub Component useState is updating in react?

I want same value in Home function from Component and Home function should not rerender when Component useState is updating.
import { useState, useEffect } from "react";
function Component() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("rerender")
})
useEffect(() => {
let interval = setInterval(() => {
setCount(Math.floor(Math.random() * 1000))
}, 1000)
return () => clearInterval(interval)
}, [])
return count
}
function OtherStuff() {
console.log("OtherStuff")
return (<h1>Should Not Rerender OtherStuff</h1>)
}
function MoreStuff() {
console.log("MoreStuff")
return (<h1>Should Not Rerender MoreStuff</h1>)
}
export default function Home() {
return (
<>
<h1>Hello</h1>
<h1>
<Component />
</h1>
<OtherStuff />
<MoreStuff />
<h1>
<Component />
</h1>
</>
);
}
I have called Component function two times, I want same value should render to both from Component.
and when ever Component {count} is update by using setCount, Main function should not rerender. at this time, there is no rerender for Component in Main function which is OK.
this is the simple problem.
Thanks.
Each instance of a component has its own state. Meaning their states are not shared. If you wanna share the state between them, one way would be to lift the state up.
If you do not want the Home to rerender, you cannot move the state to Home. So you probably need another component which holds the state. Because any component which has a react useState hook, will be rerendered when the state is updated.
You can use react context to do so. Wrap your instances of Component inside a context provider which holds the count state and provides count and setCount.
First you need to create a context:
const CountContext = createContext();
Then create a component which provides the context and holds the state:
const CountProvider = (props) => {
// `useCounter` uses `useState` inside.
const [count, setCount] = useCounter();
return (
<CountContext.Provider value={{ count, setCount }}>
{props.children}
</CountContext.Provider>
);
};
(OP provided a codesandbox which uses useCounter custom hook. It stores a number to count state and updates it to a random number every 1000ms.)
Then wrap it around your components:
function Parent() {
return (
<div>
<CountProvider>
<Counter />
<Counter />
</CountProvider>
</div>
);
}
codesandbox
Note that:
If you render another component inside CountProvider using children prop, it will NOT rerender every time count state updates, unless it uses the context.
But if you render a component inside CountProvider and render it in the function, it WILL rerender, unless you use memo.

How do you pass different props to different child components in a parent component?

I'm trying to understand the rendering in ReactJS. I'm following this tutorial from YouTube, but I want a better grasp on the subject. The video explains that if you pass down the child component
<Child/>
Then it will trigger a render of the parent and the child.
However if you pass down
{props.children}
Then it will only update the parent, if there are no changes to the children. But what if I have a series of different children? What if I have 20 children or 100? And I want to pass a prop to a specific child? Say I want to pass down a prop to the 50th child (or any other). Then how would I go about in doing this?
Say that I want to pass down these constants, would the children only re-render if they receive a prop or would they all re-render?
const [childA, setChildA] = useState("In Child A")
const [childB, setChildB] = useState("In Child B")
I tried using the code from How to pass props to {this.props.children}
{React.Children.map(props.children, function(child){
if(React.isValidElement(child)){
let clone = React.cloneElement(child, {...props})
return clone
}
But I'm not sure what I'm doing, I'm think I'm in the right path, but I'm not sure how to get what I'm looking for.
Parent component
import React, { useState } from 'react';
function ParentCount(props) {
const [count, setCount] = useState(0)
console.log("Parent render")
return (
<div>
<button onClick={()=>setCount(v=>v+1)}>Parent Increase +1</button>
Parent count is {count}
Children count
{React.Children.map(props.children, function(child){
if(React.isValidElement(child)){
let clone = React.cloneElement(child, {...props})
return clone
}
})}
</div>
);
}
export default ParentCount;
App component
function App() {
return (
<div className="App">
<ParentCount>
<ChildCount></ChildCount>
<ChildCount></ChildCount>
</ParentCount>
</div>
);
}
export default App;
Child component
import React, {useState} from 'react';
function ChildCount(props) {
const [count, setCount] = useState(0)
console.log("Child render")
return (
<div>
<button onClick={()=>setCount(v=>v+1)}>Child Increase +1</button>
<div>Child count is {count}</div>
</div>
);
}
export default ChildCount;
the meaning of the first lines you wrote is this:
if you pass a prop to a child component, react will only update that component for you. ex:
<ChildComponent prop1 = 'hi'>
if you add a whole new child component to your Component, react will update your parent too:
function ParentComponent () {
const [update, setUpdate] = useState(0);
let updateComponent = null;
if (update === 1) {
updateComponent = <UpdateComponent></UpdateComponent>;
}
return (
<div>
{ updateComponent }
</div>
)
}
sorry for bad example

react native : How to stop child component rerendering on state change?

I am developing a component where i have to change state on scrollview scroll, means on onScroll function call. I have some child components in it as well ... when I do setState parent component re-renders and also re-renders to its child components.
There is a child component having banner and description which I donot update on this screen but it also re-renders and create flickering effect which looks weird.
const [state, setState] = useState('');
setState('MOVING');
I also convert that child component from functional component to PureComponent but it still re-rendering.
There is no state update for this child component but it re-render as its parent re-render.
Please help to stop re-rendering of this child component as there is not state change in it.
Any solution to make it fix.
thanks.
Your question is related to this issue. How to stop re render child component when any state changed in react js?
To stop re-render on child components you can use React memo, or useMemo if you use react hooks.
I have same issue about rerendering on child when I used React.context with lot of unneeded data passing to child. So, u can limit what should child component refer to rerender when props change with useMemo
import React, { Component, useState, useMemo } from "react";
import { render } from "react-dom";
import Child from "./components/child.js";
import "./style.css";
function App() {
const [number, setNumber] = useState(0);
function handleOnClick() {
setNumber(number + 1);
}
//memo child not rendering when state update, only render child props update.
const memoChild = useMemo(() => <Child />, []);
// useMemo's second arguments is state variable name. example [number]. if 'number' gets update, child component rerender will be happen.
// const memoChild = useMemo(() => <Child />, [number]);
return (
<div>
<h1>Use Memo</h1>
<div className="box">
<h2>Parent Component</h2>
<h4>Number : {number}</h4>
<button onClick={() => handleOnClick()}> Increment </button>
</div>
<div className="box">
<h2>Normal child component </h2>
<Child /> {/*Every state update it gets rerendered */}
</div>
<div className="box">
<h2>Memo child Component </h2>
{memoChild}
</div>
</div>
);
}
render(<App />, document.getElementById("root"));
for more references
Re-rendering component tree is something react manages well. In terms of performance, if you are looking for a way to manage not necessary re-renders I suggest you have a look at a few useful hooks, useMemo, useCallback, and a higher order component React.memo.
With these three hooks, you are able to prevent child components from re-rendering, if parent is updated.
const Parent = () => {
const [childAState, setChildAState] = useState();
useEffect(() => {
setChildAState('Child A Updated');
}, []);
return (
<>
<ChildA childAState={childAState}/>
<ChildB />
</>
);
};
const ChildA = () => React.memo((props) => {
return <h1>Child A</h1>
});
const ChildB = () => React.memo((props) => {
return <h1>Child B</h1>
});
In the above example, ChildB wont re-render if a state in Parent does change and leads to an update in ChildA.
Further reading: React.memo

Why setState(hook) in function component cause an infinite loop?

Here is the code:
import React, { useState } from 'react'
function App() {
const [a, setA] = useState(1)
setA(2)
return (
<div>
<h1>{a}</h1>
</div>
);
}
Error. Too many re-renders. React limits the number of renders to prevent an infinite loop.
Why it can cause an infinite loop?
I think the reason is that function component just as a render function, so it will cause infinite loop when setState in render functions.
Is there any official explanation?
On each state update React will re-render the component and run all the function body, as the setA(2) is not enclosed in any hook or function and is a part of the function/component body. React will execute this on each render cycle. This make an infinite loop.
On Component mount React will set the state and go for the component update as the state updated, again there is state update React will again re-render the component. This cycle will continue until react reaches the re-render limit.
You could avoid this by wrap the state update in hook.
import React, { useState } from 'react'
function App() {
const [a, setA] = useState(1)
useEffect(() => {
setA(2)
},[]);
return (
<div>
<h1>{a}</h1>
</div>
);
}
When calling setA you actually update a state variable and trigger a re-render of you component.
When the component re-render it will call setA (just before the rendering) and will trigger a re-render again.
Can you see the infinite loop ?
Traditionally you update a state variable into a callback (i.e when the user click on a button) or under certains conditions.
In your example, you can directly set a to 2
function App() {
const [a, setA] = useState(2)
return (
<div>
<h1>{a}</h1>
</div>
);
}
If you want to have the first render with a = 1, then immediatly have a = 2, you can use an effect which will be executed only once (because of the empty array for the second argument)
function App() {
const [a, setA] = useState(2)
useEffect(() => setA(2), [])
return (
<div>
<h1>{a}</h1>
</div>
);
}
Because you are setting a new value to the state with setA(2), so each time the component is rendered the state receives a new value, forcing the component to render again
const [rows, setRows] = useState([]);
return {
<Component data= {someData}
selectCallback ={ (rows) => {
console.log(rows);
setRows(rows); // if set State going infinity loop
}}
...remainProps
/>
Its giving infinity loop when trying to setState and if we didn't use the setRows then in console giving right answers means array of objec
Help appreciated.
Because calling setA(2) causes the component to re-render.

Categories

Resources