Why child component displays updated prop value only when I'm using useState?
For example:
It works when I'm calling setReRender() with any random values.
Parent Component (demo code):
import Child from './Child';
const Parent = () => {
const [arr, setArr] = useState([]);
const [reRender, setReRender] = useState(" ");
return (
<>
{arr.map(client => {
return(
<Child
setReRender={setReRender}
arr={arr}
firstName={client.firstName}
lastName={client.lastName}
/>
);
})}
</>
)
}
The update will be displayed on the screen only after I'm calling setReRender().
For example- it works with setReRender(Math.random() * 2).
useEffect supposed to listen for update or change etc. but I didn't know that it will work with useState.
Is this the right way to make the parent component re-render itself?
Edit:
Apologies if my question isn't clear enough, it's my first time here.
The component re-renders automatically if there is a change in state, so when you are calling it with random values you are changing the state of the component and it is being re-rendered
Can you describe what your program is trying to do?
Related
In my react project
I have three components
One is called BigForm
The other two are called Form A and Form B.
In BigForm, there are two state, DataA and DataB, which would be passed into FormA and FormB.
Question:
Whenever I add input value into Form A or Form B, it will also trigger unnecessary render on the other components.
How can I avoid it? Example code would be appreciated. (please also see my edit before trying) thanks ; I heard redux may help, but I am not sure how to put this in work in this example
import React, {useState, useEffect} from "react";
const FormA = (props) => {
useEffect(()=>{ console.log('Form A was just rendered')})
const { dataA, setDataA } = props;
return (
<div>
<input onChange={(e) => setDataA(e.target.value)}></input>
<p>Input Form A{dataA}</p>
</div>
);
};
const FormB = (props) => {
const { dataB, setDataB } = props;
useEffect(()=>{ console.log('Form B was just rendered')})
return (
<div>
<input onChange={(e) => setDataB(e.target.value)}></input>
<p>Input Form B{dataB}</p>
</div>
);
};
export function BigForm (props) {
const [dataA,setDataA] = useState()
const [dataB,setDataB] = useState()
return (
<div className="App">
<FormA dataA={dataA} setDataA={setDataA}></FormA>
<FormB dataB={dataB} setDataB={setDataB}></FormB>
</div>
);
}
Edit:
For some reason, it is my intention to set the state in parent, instead of having the child to held its own state, because at the end, I need to aggregate the data from all other forms for other purpose.
.Memo is not what I want too coz in my real examples, it is not working due to there is other complexity preventing it to work.
There are a few ways to optimize re-rendering in React. The first way is shouldComponentUpdate(). This is a function that is called by React before a component is re-rendered. If shouldComponentUpdate() returns false, then the component will not be re-rendered.
The second way to optimize React components is to use PureComponent. PureComponent is a React component that is similar to Component, but it implements shouldComponentUpdate() with a shallow comparison of props and state. This means that if two values are ===, then PureComponent will not re-render the component.
The third way to optimize React components is to use React. memo(). React.memo() is a higher order component that allows you to memoize the result of a component so that it doesn't have to be re-rendered unless the props or state change.
I did a lot of research about Impure component vs Pure component, I am so confused about it and their behavior with Class & Functional components. So, please read carefully.
Question #1:
Is the definition of pure component is that "Pure component doesn't re-render when the value of changed state is the same value as the previous one", right?
Question #2 I can see that Functional Component behaves as pure component by default, because it doesn't re-renders the component if incoming state value is same as current one, am i right?.
const Home = () => {
const [count, setCount] = useState(1)
const handleCount = () => {
setCount(1)
}
console.log(`HOME ${count} - Functional Component`);
return (
<div>
<button onClick={handleCount}>Click Me</button>
<div>{count + 1}</div>
</div>
)
}
export default Home
See, It doesn't re-render when I click on button. Then it means functional components are pure components by default, right?
Question #3: Using functional component, when I set the default state to 0 and then call the setState like: setCount(1) then it should re-render only on first click because state has changed. But, when I click on 2nd time on button, it again re-renders, even current state value (1) is equal to incoming state (1), then why it re-renders on same value in functional component?
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(1)
}
I am so much confused with different definitions and different behaviors again and again.
Can someone help me with this?
Question #4: When component re-renders? On setState function call or when state value updates?
Question #5: If my default state value is true, and i call the update function like: setState(true). Will it still update the state value or it will not update the state value because it's same?
I am using callback to memoize part of component which uses useSelector value. But another state variable is changing withing the component as I am taking input, which is triggering another re render of comp, thereby useselector is getting called again and re rendering the part where I have used useCallback as selector value is there as a dependency.
How can I memorize useSelector value so that any state change change within the component does not trigger selector update unless dispatch gets called somewhere and updates the state.
const chat = useSelector((state: any) => state.user.rooms[index])
const [message, setMessage] = useState("");
chat is the selector value and message is the state for input tag.
const Component = useCallback(() => {
return (
<div className="h-[75%] bg-yellow-200 relative overflow-y-scroll flex flex-col">
{chat?.messages.map((message: any, index: number) => {
console.log(message.sentBy._id, userId);
let sentByme = message.sentBy._id === userId;
return (
<div className={"w-[50%] min-h-[20%] border-4 rounded-3xl "}>
<p>{message.text}</p>
<p>
{message.timeStamp} : {sentByme ? "me" : message.sentBy.name}
</p>
</div>
);
})}
</div>
);
}, [chat]);
I decided to set aside chat selector value inside of useCallback to prevent re render on input state change ie "message"
but re rendering is happening
Components rerender as a whole, and per default all their children will re-render after that as well. If you want a part of your component not to rerender, you need to split that into two components and render them side-by-side or wrap the child component in React.memo.
Selectors will be executed on each render and also on every Redux dispatch - if that is a performance problem at some point, look into memoized selectors. https://redux.js.org/recipes/computing-derived-data
In your case the selector itself is not a performance problem at all though.
I am learning Reactjs. And I am building Counter App. I think the code below and state in react
are the same. console.log print correctly but in the html it doesn't change the value. Can you explain to me why the code didn't work?
import React, {useState} from "react";
let count = 0;
const Counter = (props) => {
const setCount = ()=> {
count = count + 1;
};
return (
<div>
<button onClick={()=> {
setCount();
console.log('counter: ', count);
}}>button</button>
<div>Hello {props.name} and your current number is: {count} </div>
</div>
)
};
export default Counter;
Changing a variable does not rerender the component.
To see updates in the dom you must rerender a component. There are a few ways to do this.
changing the state
changing the props
changing the context
if you put the count variable in a state and trigger it from there you will see it update in the component
Example form the docs
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
html should be rendered with the new value once button is clicked, this is where the state and setstate methods comes to play. setcount means setcount will change the value and refreshes or say rerender the html with the value.
Well.. that's why state is there for.. It essentially tells your component to REACT, to its change.
If you want your DOM to respond to the change of a variable, You need to bind it to a state variable.
Your code below, will only change the variable, but won't trigger any re-rendering. That's why you could see the change in console, but not in DOM.
const setCount = ()=> {
count = count + 1;
};
First of all, according to React docs, a component re-renders only when a state or a props(used in the component) updates. So change in a variable doesn't affect the rendering of the component. If variable change affects the component, then there would be no need of using this library. Learn about setState. Hope it helps!.. Happy coding! ..
I have a problem with ref and conditional rendering.
I would like to focus an input tag when I click on a button tag.
Basically, I have this simplified code.
class App extends React.Component {
textInput
constructor(props) {
super(props)
this.state = {isEditing: false}
this.textInput = React.createRef()
}
onClick = () => {
this.setState({isEditing: !this.state.isEditing})
this.textInput.current.focus();
}
render () {
let edit = this.state.isEditing ?
(<input type="text" ref={this.textInput} />)
: ""
return (
<div>
<button onClick={this.onClick}>lorem </button>
{edit}
</div>
);
}
}
When I click on the button, the input tag is displayed but the ref textInput is still set to null. Thus I can't focus the input.
I found some workaround like:
set autoFocus property in the input tag
hide the input tag with css when isEditing == false
But actually it is a very basic pattern and I would like to know if there is a clean solution.
Thank you
TL;DR:
Change this:
this.setState({isEditing: !this.state.isEditing})
this.textInput.current.focus();
to this:
this.setState(previousState => ({isEditing: !previousState.isEditing}), () => {
this.textInput.current.focus();
});
Update: Functional Components / Hooks
It's been asked in the comments how to do this with useState and functional components. Rafał Guźniczak's answer explains it, but I wanted to provide a bit more explanation and a runnable example.
You still don't want to read state immediately after setting it, but instead of using a second argument callback to setState, you need to run some code after the state is updated and the component has re-rendered. How do we do that?
The answer is useEffect. The purpose of effects are to synchronize external "things" (for example: imperative DOM things like focus) with React state:
const { useEffect, useRef, useState } = React;
const { render } = ReactDOM;
function App(props) {
const [isEditing, setIsEditing] = useState(false);
const textInputRef = useRef(null);
const toggleEditing = () => setIsEditing(val => !val);
// whenever isEditing gets set to true, focus the textbox
useEffect(() => {
if (isEditing && textInputRef.current) {
textInputRef.current.focus();
}
}, [isEditing, textInputRef]);
return (
<div>
<button onClick={toggleEditing}>lorem </button>
{isEditing && <input type="text" ref={textInputRef} />}
</div>
);
}
render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Details:
You're running into a common problem many people run into with React, which is the assumption that setting state is synchronous. It's not. When you call setState, you're requesting that React update the state. The actual state update happens later. This means that immediately after the setState call, the edit element hasn't been created or rendered yet, so the ref points to null.
From the docs:
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.
Thank a lot for your answer #rossipedia. I was wondering if I can do it with hooks.
And apparently you can't pass second parameter to useState setter as in setState. But you can use useEffect like this (note second parameter in useEffect):
const [isEditing, setIsEditing] = React.useState(false);
React.useEffect(() => {
if (isEditing) {
textInput.current.focus();
}
}, [isEditing]);
const handleClick = () => setIsEditing(isEditing);
And it worked! ;)
Source: https://www.robinwieruch.de/react-usestate-callback/