I have a component, I set a count and let state update when button clicked.
But when I check render times, it renders twice each time I click the button.
https://codesandbox.io/s/brave-forest-yre6y?file=/src/App.js
export default function App() {
const cute = Array(10).fill({});
const [count, setCount] = useState(2);
console.log(count);
return (
<div className="App">
<button
onClick={() => {
if (count < 10) setCount(count + 1);
}}
>
add
</button>
{cute.map((data, index) => {
if (index < count) {
return (
<div>
<p>{index}. Luna is cute</p>
</div>
);
}
})}
</div>
);
}
I wonder:
Why does it work like this?
How could I prevent this?
Your app is using StrictMode. See your index.js file - your app is wrapped between a <React.StrictMode> element.
Using StrictMode will cause your app to render twice, but only in development mode. Creating an app with create-react-app will enable strict mode by default.
Here are the official docs for strict mode.
The solution is just to remove <React.StrictMode>, but that will also disable some of its advantages, so if it doesn't bother you, I'd just leave it as is, knowing it won't render like that in production.
See this related question for more details: My React Component is rendering twice because of Strict Mode
Obviously that re-rendering thing is definitely not a bug, or something related with the library's render mechanism. On the contrary it is a debugging mechanism provided by React 🤗
refer this blog https://mariosfakiolas.com/blog/my-react-components-render-twice-and-drive-me-crazy/
you'll get better understanding .
you can disable strictmode refer this sandbox link.it'll only render once .
https://codesandbox.io/s/snowy-violet-eo70o?file=/src/index.js
Related
This is what I am trying to do:
import { SVG } from '#svgdotjs/svg.js'
const SVGpaper = (props) => {
useEffect(() => {
let canvas = SVG().addTo('#canvas').size(6006, 600);
canvas.rect(100, 100);
});
return (
<div id="canvas">
</div>
)
}
const Profile = () => {
return (
(typeof someVariable !== "undefined") &&
<CCol>
<CRow>
<div>
{SVGpaper()}
</div>
</CRow>
</CCol>
);
};
export default Profile;
Profile is the "big" webpage, it will fetch some data from a server to "someVariable". To make sure it is not rendered prematurely it (Profile) has conditional rendering. When it is eventually rendered I want to create an SVG. I have therefore created a component that creates the SVG and put it into the render of Profile. Because SVG().addTo must be executed after the div id="canvas" actually exist, it is put in a useEffect to prevent it from executing before the div exist. This works great in my head, but not in reality because it breaks the rules of hooks. I get the error:
"Warning: React has detected a change in the order of Hooks called by Profile. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks"
And I should know this, but I am having issues making this code work, so I am trying a little bit of every idea I get, but without any success. How can I change this code and make it render in this order:
conditional rendering for "someVariable"
render the div with id="canvas"
Create the SVG and assign it to the div with id="canvas"
?
Thank you for your help!
I'm trying to conditionally render a div in Gatsby in an effort to build a responsive nav menu. Unfortunately, I'm getting a quick flash of the menu div just before the full navigation menu loads. Any tips or tricks to resolve this would be appreciated!
import React, { useEffect, useState } from "react"
import * as navlinksStyles from "./navlinks.module.scss"
const NavLinks = () => {
const [windowDimension, setWindowDimension] = useState(null)
useEffect(() => {
setWindowDimension(window.innerWidth)
}, [])
useEffect(() => {
function handleResize() {
setWindowDimension(window.innerWidth)
}
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
}, [])
const isMobile = windowDimension <= 740
return (
<div className={navlinksStyles.wrapper}>
{isMobile ? (
<div>
<h1 className={navlinksStyles.menu}>menu</h1>
</div>
) : (
<div className={navlinksStyles.navlinksWrapper}>
<ul>
<li>About</li>
<li>Services</li>
<li>Frames</li>
<li>Lenses</li>
<li>Locations</li>
<li>Mailbox</li>
</ul>
</div>
)}
</div>
)
}
export default NavLinks
Gatsby does server-side rendering, which involves rendering your React components in a Node environment and saving out the markup produced as a static file. When someone visits one of your pages in production (or in development if you're using SSR in dev), React renders your components and associates them with the already-visible DOM nodes in a process referred to as “rehydration”.
This is all important to know because useEffect (or class-based API methods like componentDidMount) don't run during SSR. They only run once the code is rehydrated client-side. Further, if the DOM nodes produced server-side don't match up with what React renders client-side on the initial render (before any useEffect hooks run), you wind up with a hydration mismatch error that prompts React to throw away the DOM nodes that exist and replace them with what it has produced client-side.
Armed with this info, you can start to debug what might be happening to cause a flash of unexpected content and how to address it:
Server-side, windowDimension is null, and null <= 740 is true, so isMobile is set to true
The output produced server-side then shows the div>h1 element that you're expecting mobile visitors to see
Client side, React rehydrates and fires useEffect hooks, the first of which calls a state setter passing a number greater than or equal to (presumably) 740, prompting a re-render
The component re-renders with the updated value and isMobile is set to false, updating the output to the full nav menu
Once approach to solving this is to wait to render markup or render a placeholder until you’re rendering in a browser:
// Note: do NOT do this!
const NavLinks = () => {
if (typeof window === "undefined") return null
return (
<div>Your content</div>
)
}
The problem with this, as alluded to above, is that you wind up producing different markup server-side than you do in a browser, causing React to throw away DOM nodes and replace them. Instead, you can ensure the initial render matches the server-side output by leveraging useEffect like so:
const NavLinks = () => {
const [ready, setReady] = useState(null)
useEffect(() => { setReady(true) }, [])
// note: the return value of an `&&` expression is the value of the first
// falsey condition, or the last condition if all are truthy, so if `ready`
// has not been updated, this evaluates to `return null`, and otherwise, to
// return <div>Your content</div>.
return ready && <div>Your content</div>
}
There is another approach that works in many scenarios that will avoid the extra render, DOM update/layout/paint cycle: use CSS to hide one of the DOM branches as relevant:
/** #jsx jsx */
import { jsx } from "#emotion/core"
const mobile = "#media (max-width: 740px)"
const NavLinks = () => (
<div>
<div css={{ display: "none", [mobile]: { display: "block" } }}>
Mobile menu
</div>
<div css={{ [mobile]: { display: "none" } }}>
Desktop menu
</div>
</div>
)
I prefer this approach when possible as it cuts down on layout thrashing on-load, but if the DOM trees are large, the extra markup can slow things down as well. As with anything, do some testing for your own use cases and select what works best for you and your team.
Your page is rendered with windowDimension as null, so when someone with a wider windowDimension visit your page, they'll see a brief flash of mobile layout before React kicks in & render the correct one.
You can get around this by using #media query instead.
I understand that, within your React component, it's always good to render a component rather than use a function that renders some JSX.
What I mean is,
Doing this :
export default function App() {
const [count, setCount] = useState(0);
const someRenderFunction = () => <p>Hey</p>;
return (
<div className="App">
{someRenderFunction()}
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
Increment{" "}
</button>
<p>{count}</p>
</div>
);
}
is NOT encouraged. The render function should be exported into it's own Component, like this :
const HeyComponent = () => <p>Hey</p>
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<HeyComponent />
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
Increment{" "}
</button>
<p>{count}</p>
</div>
);
}
But I never really understood the benefits of this refactor. I tried placing checking the re-render behaviours, unmount behaviours. Still didn't see any difference.
Can anyone explain in detail why this refactor is necessary/beneficial ?
This is all about components, so components should be reusable and should follow the DRY principle, in your case that seems to be so simple and just as you said will prevent the someRenderFunction() to be re-rendered if there aren't any changes to that component in the virtual dom so the best practices are to use <X /> syntax always or for some cases const Y = <X /> also is more readable. testing is another reason to create more components and make them more decoupled. imagine here you need to pass props to your someRenderFunction() so you will lose the jsx feature for passing props as <X prop={PROP}/>.
The actual difference between the two is that the function returning JSX is not a component, so it does not have its own state. When you use useState in the function, the state change will cause the App to be re-rendered.
Two reasons why the second way is better than the first way are:
The function which defines the <p>Hey</p> JSX (named someRenderFunction in the first way, and HeyComponent in the second) is moved outside of the component App in the second way. As of now, there's no benefit, but if you later wanted to extend someRenderFunction/HeyComponent to have normal component lifecycle that can be hooked into, moving it outside of App would be essential. For example, if you wanted the function/component to keep track of its own state, the first way would cause someRenderFunction's state to get reset every time App renders. More info on this problem can be found here: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md, and here: https://stackoverflow.com/a/64211013/693855.
HeyComponent would be seen as a valid component whereas someRenderFunction would not, because component names need to start with a capital letter (this is mentioned here https://reactjs.org/docs/components-and-props.html#rendering-a-component in the Note at the bottom of the section). If you used hooks in your function, and it was not seen as a valid component by React, it would be breaking the "Rules of Hooks" set by React (https://reactjs.org/docs/hooks-rules.html#only-call-hooks-from-react-functions). I'm not sure if this would cause any bugs, but it is frowned upon by React -- if you have the "react-hooks/rules-of-hooks" ESLint rule enabled (from https://www.npmjs.com/package/eslint-plugin-react-hooks), you would see an error like this: ESLint: React Hook "useState" is called in function "someRenderFunction" that is neither a React function component nor a custom React Hook function.(react-hooks/rules-of-hooks)
The code which renders a component is followed as best practice.
Using Components we can pass state values to child component and can be used to display data. The same component can be re-used in other places.
If you have to just display a small function it can be used too if it shows constant information.
I have a component that dispatches some actions when a state somewhere else in the app changes.
But this is somehow giving me the dreaded change in the order of Hooks error.
In reading the rules of hooks it says basically wrapping hooks in conditionals is verboten... Which I am not currently doing.
However I am rendering lists of items and the values of those lists changes, as the server loads data. Using selectors and redux-sagas makes all of this pretty impossible to actually debug, so how else might I track down the root of the problem?
I have one component:
const PhraseList = props => {
const trainingPhrases = useSelector(selectFilteredPhrases());
return (
<div className={styles.Wrapper}>
<ul className={opStyles.listReset}>
{
trainingPhrases.map((phrase, idx) => {
return PhraseItem({ phrase, idx });
})
}
</ul>
</div>
);
which calls the PhraseItem component, which throws an Error.
const PhraseItem = ({ phrase, idx }) => {
// this line chokes
const [checked, setChecked] = useState(false);
return (
<li key={"tr-" + idx}>
<div className={styles.container}>
<div className={styles.phraseText}>{phrase.text}</div>
</div>
</li>
);
};
I'm debugging changes in the selector (selector factory) and that is what seems to trigger the app to crash.
export const selectFilteredPhrases = () =>
createSelector(trainingPhrases, phraseFilter, (trainingPhrases, phraseFilter) => {
console.log('trainingPhrases', trainingPhrases)
return trainingPhrases
});
but if data didn't change ever, it's hard to buidl an app :O
I saw an answer about using JSX which I don't think is relevant. And not much else on this issue.
redux, sagas, hooks, selectors... the react DSL is more and more layers and it's hard to debug deep in the framework.
UPDATE. I think this might be because I'm calling two sagas one after the other and this causes some kind of race condition in the UI?
const pickIntent = () => {
dispatch(pickIntentAction(intentId));
dispatch(getIntentPhrasesAction(intentId));
I think this answer is related, as it is already mentioned in this link, if you call the jsx component as a function react thinks that, hooks which are declared in this jsx function is part of the app component, so if your array trainingPhrases has fewer element on the first render than on the second one it will cause this warning (there will be less rendered jsx components, which means there will be less hooks). solution is simple call jsx component like this <PhraseItem phrase={phrase} idx={idx} />
I occasionally have react components that are conceptually stateful which I want to reset. The ideal behavior would be equivalent to removing the old component and readding a new, pristine component.
React provides a method setState which allows setting the components own explicit state, but that excludes implicit state such as browser focus and form state, and it also excludes the state of its children. Catching all that indirect state can be a tricky task, and I'd prefer to solve it rigorously and completely rather that playing whack-a-mole with every new bit of surprising state.
Is there an API or pattern to do this?
Edit: I made a trivial example demonstrating the this.replaceState(this.getInitialState()) approach and contrasting it with the this.setState(this.getInitialState()) approach: jsfiddle - replaceState is more robust.
To ensure that the implicit browser state you mention and state of children is reset, you can add a key attribute to the root-level component returned by render; when it changes, that component will be thrown away and created from scratch.
render: function() {
// ...
return <div key={uniqueId}>
{children}
</div>;
}
There's no shortcut to reset the individual component's local state.
Adding a key attribute to the element that you need to reinitialize, will reload it every time the props or state associate to the element change.
key={new Date().getTime()}
Here is an example:
render() {
const items = (this.props.resources) || [];
const totalNumberOfItems = (this.props.resources.noOfItems) || 0;
return (
<div className="items-container">
<PaginationContainer
key={new Date().getTime()}
totalNumberOfItems={totalNumberOfItems}
items={items}
onPageChange={this.onPageChange}
/>
</div>
);
}
You should actually avoid replaceState and use setState instead.
The docs say that replaceState "may be removed entirely in a future version of React." I think it will most definitely be removed because replaceState doesn't really jive with the philosophy of React. It facilitates making a React component begin to feel kinda swiss knife-y.
This grates against the natural growth of a React component of becoming smaller, and more purpose-made.
In React, if you have to err on generalization or specialization: aim for specialization. As a corollary, the state tree for your component should have a certain parsimony (it's fine to tastefully break this rule if you're scaffolding out a brand-spanking new product though).
Anyway this is how you do it. Similar to Ben's (accepted) answer above, but like this:
this.setState(this.getInitialState());
Also (like Ben also said) in order to reset the "browser state" you need to remove that DOM node. Harness the power of the vdom and use a new key prop for that component. The new render will replace that component wholesale.
Reference: https://facebook.github.io/react/docs/component-api.html#replacestate
The approach where you add a key property to the element and control its value from the parent works correctly. Here is an example of how you use a component to reset itself.
The key is controlled in the parent element, but the function that updates the key is passed as a prop to the main element. That way, the button that resets a form can reside in the form component itself.
const InnerForm = (props) => {
const { resetForm } = props;
const [value, setValue] = useState('initialValue');
return (
<>
Value: {value}
<button onClick={() => { setValue('newValue'); }}>
Change Value
</button>
<button onClick={resetForm}>
Reset Form
</button>
</>
);
};
export const App = (props) => {
const [resetHeuristicKey, setResetHeuristicKey] = useState(false);
const resetForm = () => setResetHeuristicKey(!resetHeuristicKey);
return (
<>
<h1>Form</h1>
<InnerForm key={resetHeuristicKey} resetForm={resetForm} />
</>
);
};
Example code (reset the MyFormComponent and it's state after submitted successfully):
function render() {
const [formkey, setFormkey] = useState( Date.now() )
return <>
<MyFormComponent key={formkey} handleSubmitted={()=>{
setFormkey( Date.now() )
}}/>
</>
}
Maybe you can use the method reset() of the form:
import { useRef } from 'react';
interface Props {
data: string;
}
function Demo(props: Props) {
const formRef = useRef<HTMLFormElement | null>(null);
function resetHandler() {
formRef.current?.reset();
}
return(
<form ref={formRef}>
<input defaultValue={props.data}/>
<button onClick={resetHandler}>reset</button>
</form>
);
}