I am declaring a new variable called name and initializing it with useState to Math.Random() I then log the variable in 2 places, once in the body of the component, and once in the useEfect of the component. What I'm seeing is that the variable is changing.
My name is 0.1869175357793944 here
My name is 0.10608469707774937 after render
Am I doing something wrong here? I cannot figure out why it's behaving like this.
Here is my code:
const {useState} = React;
const {useEffect} = React;
const Example = () => {
const [name, setName] = useState(Math.random());
console.log(`My name is ${name} here`);
useEffect(() => {
console.log(`My name is ${name} after render`);
}, [])
return (
<div>Look at console</div>
);
};
// Render it
ReactDOM.render(
<React.StrictMode>
<Example/>
</React.StrictMode>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<div id="react"></div>
What you are seeing is an unintentional side-effect from the console.log in the component body. When you render your app inside a React.StrictMode component some functions are double-invoked to help you detect these unexpected side-effects.
const Example = () => {
const [name, setName] = useState(Math.random());
console.log(`My name is ${name} here`); // <-- unexpected side effect!!
useEffect(() => {
console.log(`My name is ${name} after render`);
}, [])
return (
<div>Look at console</div>
);
};
StrictMode - Detecting Unexpected Side Effects
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies (emphasis mine)
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
Additionally
Note:
Starting with React 17, React automatically modifies the console
methods like console.log() to silence the logs in the second call to
lifecycle functions. However, it may cause undesired behavior in
certain cases where a workaround can be used.
The additional note explains why you only see ONE "My name is 0.1869175357793944 here" log instead of two, each with a different random number value.
Here's a codesandbox running React v16.13.0 where the second log has not been stifled yet. Notice two unexpected logs with different values. Notice also that the result of the second call is that you see when the component actually rendered, i.e. what you see logged in the useEffect hook.
My name is 0.859128653421811 here
My name is 0.6861295664325067 here
My name is 0.6861295664325067 after render
Actually, this behavior will only happen in the development mode, not in production. In production, it will show a single value.
Reason: React applications are wrapped with <React.StrictMode /> component.
In short, strict mode help to cache any issue that occurs during development.
Note:
Strict mode checks are run in development mode only; they do not impact the production build. https://reactjs.org/docs/strict-mode.html
Just for testing, try to remove <React.StrictMode>...</React.StrictMode>. Your problem will not exist anymore in the development mode as well.
ReactDOM.render(
<Example/>
document.getElementById("react")
);
I believe the problem is that you are assigning name a value of the Math.random function instead of the result of Math.random().
Try this:
const [name, setName] = useState(Math.random());
I don't sure but it may work for you
const {useState} = React;
const {useEffect} = React;
const number=Math.random()
const Example = () => {
const [name, setName] = useState(number);
console.log(`My name is ${name} here`);
useEffect(() => {
console.log(`My name is ${name} after render`);
}, [])
return (
<div>Look at console</div>
);
};
// Render it
ReactDOM.render(
<React.StrictMode>
<Example/>
</React.StrictMode>,
document.getElementById("react")
);
Related
I know there are other articles and posts on this topic and almost all of them say to use the ! operator for a Boolean state value. I have used this method before but for the life of me I can not toggle this Boolean value.
import { useState } from 'react';
const [playerTurn, setPlayerTurn] = useState(true);
const changePlayerTurn = () => {
console.log(playerTurn); // returns true
setPlayerTurn(!playerTurn);
console.log(playerTurn); // also returns true
};
changePlayerTurn();
I have also tried setPlayerTurn(current => !current), commenting out the rest of my code to avoid interference, and restarted my computer in case that would help but I am still stuck with this issue.
Can anyone point out why this is not working?
The setPlayerTurn method queues your state change (async) so reading the state directly after will provide inconsistent results.
If you use your code correctly in a react component you will see that playerTurn has changed on the next render
You creating a async function, to solve this you can create a button in your component, which will run the function and you can use the "useEffect" hook to log every time the boolean changes... so you can see the changes taking place over time, like this:
import React, { useEffect } from "react";
import { useState } from "react";
const Player = () => {
const [playerTurn, setPlayerTurn] = useState(true);
useEffect(() => {
console.log(playerTurn);
}, [playerTurn]);
return <button onClick={() => setPlayerTurn(!playerTurn)}>change player turn</button>;
};
export default Player;
This is happening because setPlayerTurn is async function.
You can use another hook useEffect() that runs anytime some dependencies update, in this case your playerTurn state.
export default YourComponent = () => {
const [playerTurn, setPlayerTurn] = useState(true);
useEffect(() => {
console.log('playerTurn: ', playerTurn);
}, [playerTurn]);
const changePlayerTurn = () => {
setPlayerTurn(!playerTurn);
}
return (
<button onClick={changePlayerTurn}>Click to change player turn</button>
);
}
Basically whenever you use setState React keeps a record that it needs to update the state. And it will do some time in the future (usually it takes milliseconds). If you console.log() right after updating your state, your state has yet to be updated by React.
So you need to "listen" to changes on your state using useEffect().
useEffect() will run when your component is first mounted, and any time the state in the dependencies array is updated.
The value of the state only changes after the render. You can test this like:
// Get a hook function
const Example = ({title}) => {
const [playerTurn, setPlayerTurn] = React.useState(true);
React.useEffect(() => {
console.log("PlayerTurn changed to", playerTurn);
}, [playerTurn]);
console.log("Rendering...")
return (<div>
<p>Player turn: {playerTurn.toString()}</p>
<button onClick={() => setPlayerTurn(!playerTurn)}>Toggle PlayerTurn</button>
</div>);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
The callback inside the useEffect runs during the component mount and when one of the values inside the second argument, the dependecy array, changes. The depency here is playerTurn. When it changes the console will log.
As you will see, before this happens, the "Rendering..." log will appear.
So I have a functional component that displays an iframe. The src-property for the iframe is set in useState().
Here is the code:
import React, { useEffect, useState } from "react";
export default function ComponentWithIframe() {
const [iframeUrl, setIframeUrl] = useState(undefined);
useEffect(() => {
// some axios-requests
... setIframeUrl(responseFromAxios);
});
return (
<div className='Iframe'>
<iframe
id="my_iframe"
src={iframeUrl}
width="100%" height='800px'
title="I am an Iframe"/>
</div>
);
}
Now here's the strange thing: If useState() is initialized with a string, the component does multiple re-renders on mount (3 times). For example:
const [iframeUrl, setIframeUrl] = useState("There is no url defined");
But when I initialize useState() with undefined, then the component is rendered only once and everything works fine.
I found this solution more by accident and don't understand why it is working like this. Research didn't bring any explanation. Neither the official React Docs nor in-depth explanation sites like this or this. Even here on StackOverflow, I couldn't find anything.
Does anyone know what causes this behavior?
I think the re-render occurs because there is no dependency array.
It should more like this,
useEffect(() => {
// some axios-requests
... setIframeUrl(responseFromAxios);
}, []) // <- This Array
Refer: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
could someone explain to me if the use of useMemo and useCallback is expensive or cheap? I've checked the React's source code and I think they are cheap to use, but I've also read some people saying they are not.
In a Next.js context using useCallback:
const MyComp = () => {
const router = useRouter():
const handleClick = useCallback(() => router.push('/some-path'), [router]);
return <button onClick={handleClick} />
}
vs plain arrow function here:
const MyComp = () => {
const router = useRouter():
return <button onClick={() => router.push('/some-path')} />
}
Am I saving re-renders with useCallback?
The cost of memoize and comprare the dependencies array [router], is more expensive?
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Also checking this component render:
import {useCallback, useState} from "react";
let i = 0
const CallbackDemo = () => {
const [value, setValue] = useState('');
console.log('render', i++);
const handleClick = useCallback(() => setValue(Math.random()), []);
return (
<div>
<span>{value}</span>
<button onClick={handleClick}>New set</button>
</div>
);
}
export default CallbackDemo;
I saw the same count of renders using or not using useCallback
Am I saving re-renders with useCallback?
Not in that specific case, no. The useCallback code does let you save roughly the cost of an assignment statement because it doesn't have to update the click handler on the button element, but at the cost of a function call and going through the array of dependencies looking for differences. So in that specific case, it's probably not worth doing.
If you had a more complex child component, and that component was optimized not to re-render when called with the same props (for instance, via React.memo or React.PureComponent or similar), or you were passing the function to a bunch of child components, then you might get some performance improvements by not making them re-render.
Consider this example, where simpleClickHandler is always recreated but memoizedClickHandler is reused¹ via useCallback:
const { useState, useCallback } = React;
const Parent = () => {
const [counter, setCounter] = useState(0);
console.log("Parent called");
const simpleClickHandler = () => {
console.log("Click occurred");
setCounter(c => c + 1);
};
const memoizedClickHandler = useCallback(() => {
console.log("Click occurred");
setCounter(c => c + 1);
}, []);
return (
<div>
Count: {counter}
<Child onClick={simpleClickHandler} handlerName="simpleClickHandler">simpleClickHandler</Child>
<Child onClick={memoizedClickHandler} handlerName="memoizedClickHandler">memoizedClickHandler</Child>
</div>
);
};
const Child = React.memo(({handlerName, onClick, children}) => {
console.log(`Child using ${handlerName} called`);
return (
<div onClick={onClick}>{children}</div>
);
});
ReactDOM.render(<Parent/>, document.getElementById("root"));
<div id="root"><?div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Note that when you click, only the child being passed simpleClickHandler re-renders, not the one being passed memoizedClickHandler, because memoizedClickHandler's value is stable but simpleClickHandler's value changes every time.
Using useCallback requires more work in the parent (checking to see if the previous function can be reused), but may help save work in child components.
It's important to note that the stability useCallback (and useMemo) give you are only appropriate for performance reasons, not correctness. React can throw away the previous copy of a handler and new a new one if it wants to, even if the deps haven't changed. If you need a correctness guarantee (the result definitely 100% will not change unless the deps change), you have use a ref. But that's a very rare use case.
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Primarily because NaN === NaN is false, because all comparisons with NaN are false. So if they used ===, any deps array containing NaN would always be considered different from the previous one. But using Object.is avoids that problem, because Object.is(NaN, NaN) is true.
¹ Note that "reused" here means: a new handler is created every time, just like simpleClickHandler, but useCallback may return the previous handler you've given it if the deps haven't changed, so the new one is thrown away. (JavaScript engines are really quick at allocating and reclaiming short-lived objects.)
useCallback like useMemo indeed improve performance
but!!! you don't need to use it to everything because it will make your website slower.
this is better for the heavy lifting components like for charts and stuff like that.
that consume a lot of resource and take a lot of time to process and this will make this specific component load much more faster and not stuck everytime you do a change.
you can see deep compression like this:
react useEffect comparing objects
this link will show you for instance how to do a deep compression for use effect
NB: I've asked this on wordpress.stackexchange, but it's not getting any response there, so trying here.
I'm not sure if this is WordPress specific, WordPress's overloaded React specific, or just React, but I'm creating a new block plugin for WordPress, and if I use useState in its edit function, the page is re-rendered, even if I never call the setter function.
import { useState } from '#wordpress/element';
export default function MyEdit( props ) {
const {
attributes: {
anAttribute
},
setAttributes,
} = props;
const [ isValidating, setIsValidating ] = useState( false );
const post_id = wp.data.select("core/editor").getCurrentPostId();
console.log('Post ID is ', post_id);
const MyPlaceholder = () => {
return(
<div>this is a test</div>
);
};
const Component = MyPlaceholder;
return <Component />;
}
If I comment out const [ isValidating, setIsValidating ] = useState( false ); then that console.log happens once. If I leave it in, it happens twice; even if I never check the value of isValidating, never mind calling setIsValidating. I don't want to over-optimize things, but, equally, if I use this block n times on a page, the page is getting rendered 2n times. It's only on the admin side of things, because it's in the edit, so maybe not a big deal, but ... it doesn't seem right. Is this expected behavior for useState? Am I doing something wrong? Do I have to worry about it (from a speed perspective, from a potential race conditions as everything is re-rendered multiple times)?
In your example code, the console.log statement is being immediately evaluated each time and triggering the redraw/re-rendering of your block. Once console.log is removed, only the state changes will trigger re-rendering.
As the Gutenberg Editor is based on Redux, if the state changes, any components that rely on that state are re-rendered. When a block is selected in the Editor, the selected block is rendered synchronously while all other blocks in the Editor are rendered asynchronously. The WordPress Gutenberg developers are aware of re-rendering being a performance concern and have taken steps to reduce re-rendering.
When requesting data from wp.data, useEffect() should be used to safely await asynchronous data:
import { useState, useEffect } from '#wordpress/element';
export default function MyEdit(props) {
...
const [curPostId, setCurPostId] = useState(false);
useEffect(() => {
async function getMyPostId() {
const post_id = await wp.data.select("core/editor").getCurrentPostId();
setCurPostId(post_id);
}
getMyPostId();
}, []); // Run once
const MyPlaceholder = () => {
return (
<div>Current Post Id: {curPostId}</div>
);
};
const Component = MyPlaceholder;
return <Component />;
}
As mentioned in the question, useState() is used in core blocks for setting and updating state. The state hook was introducted in React 16.8, its a fairly recent change and you may come across older Gutenberg code example that set state via the class constructor and don't use hooks.
Yes, you have to worry about always put an array of dependencies, so that, it won't re-render, As per your query, let's say are planning to edit a field here is the sample code
const [edit, setEdit]= useState(props);
useEffect(() => {
// logic here
},[edit])
that [edit] will check if there is any changes , and according to that it will update the DOM, if you don't put any [](array of dependencies) it will always go an infinite loop,
I guess this is expected behavior. If I add a similar console.log to native core blocks that use useState, I get the same effect. It seems that WordPress operates with use strict, and according to this answer, React double-invokes a number of things when in strict mode.
Can useMemo be used just to avoid extra referential equality checking code/vars when setting state during a render?
Example: useMemo with a setState during render taken from this rare documented use case:
function ScrollView({row}) {
let [isScrolling, setIsScrolling] = useState(false);
const lessCodeThanCheckingPrevRow = useMemo(
() => {
// Row changed since last render. Update isScrolling.
setIsScrolling(true); // let's assume the simplest case where prevState isn't needed here
},
[row]
);
return `Scrolling down: ${isScrolling}`;
}
Above drastically reduces code and extra vars, only otherwise used for equality checks, so why do the docs imply referential equality checks should be done manually?
This seems to be an elegant way to reduce boiler plate to me. I created a codesandbox to validate its behaviour.
const UnitUnderTest = ({prop}) => {
let [someState, setSomeState] = useState(false);
const lessCodeThanCheckingPrevRow = useMemo(
() => setSomeState(current => !current),
[prop],
);
useEffect(() => console.log('update finished'), [prop])
console.log('run component');
return `State: ${someState}`;
}
const App = () => {
const [prop, setProp] = useState(false);
const handleClick = () => setProp(current => !current);
return (
<div>
<button onClick={handleClick} >change prop</button>
<UnitUnderTest prop={prop} />
</div>
)
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Output when clicking the button to change the prop passed to the component:
> run component
> run component
> update finished
As you can see the component has been run twice before the update cycle completed. This is equivalent to the the behaviour of getDerivedStateFromProps.
I guess that there is no deeper thought behind why the docs propose a slightly different technique. In a way this is a manual check too but in a neat way. +1 for the idea.
Use the useEffect hook for this behavior. useMemo is used to store a value that might not necessarily change over each renders, so that you avoid useless re-calculation of that value
if the problem is having a value that changes on every user action which in this case is scrolling, then useMemo is not the best way to go. Perhaps useCallback is a better option. The code block would be the same as you have defined it above, but instead of useMemo, useCallback, and include the state in the dependency array.