Why does react compute render twice before initial load? - javascript

I was under the impression that react render computes once and compares it with DOM to make the change?
However, from my simple example: https://codesandbox.io/s/admiring-rain-ej8fu?file=/src/App.js it seems even before the component mounts. The render function is exceuted twice. I've also included a screenshot below to clarify my point. In the console you can see console.log(value) is evaluated twice. I wonder what conceptual mistake I'm making here!
Code:
import React, { useState, useEffect } from "react";
function Load() {
const [value, setValue] = useState("intial Value");
console.log(value);
useEffect(() => {
console.log("mounted");
}, []);
return (
<div>
<h1>{value}</h1>
<button onClick={e => setValue(Math.random())}>Update state</button>
</div>
);
}
export default Load;
Screenshot:
react initial reload error

It is because codesandbox uses React Strict Mode. React Strict Mode is for better debugging, so, after rendering it reruns all the lifecycle methods to make sure they're "safe". You can reead more about it here. So, just remove React.StrictMode tag in index.js file.

Related

Re-rendering in React functional component - why this behavior?

This question concerns the definition of callback functions inside functional components, with the callback functions being passed down as props to child functional components.
I am aware that when defining functions inside functional components and those functions are being passed on as props to child components we should use useCallback. In this example I am deliberately not using it.
I have a dummy React app as below:
App.jsx
import { useState, useCallback } from 'react';
import './App.css';
import TestComponent from './components/TestComponent';
import TestComponent2 from './components/TestComponent2';
function App() {
const [message, setMessage] = useState("");
console.log('Defining myArrowfunc');
const myArrowFunc = () => console.log('Hello!');
console.log('myArrowfunc defined');
return (
<>
<TestComponent message={message} myArrowFunc={myArrowFunc} />
<TestComponent2 myArrowFunc={myArrowFunc} />
<input
type="text"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button
type="button"
onClick={() => {
console.log('Setting message');
setMessage("x");
}}>Set to x</button>
</>
);
}
export default App;
TestComponent.jsx
import { useEffect } from 'react';
function TestComponent(props) {
useEffect(() => {
console.log(`TestComponent.useEffect, props.message = ${props.message}`);
}, [props]);
return <h2>TestComponent - {props.message}</h2>;
}
export default TestComponent;
TestComponent2.jsx
import { useEffect } from 'react';
function TestComponent2(props) {
useEffect(() => {
console.log(`TestComponent2.useEffect`);
}, [props.myArrowFunc]);
return <h2>TestComponent2 - Placeholder</h2>;
}
export default TestComponent2;
Starting the app, the page loads and we are here:
In the console window everything looks like expected. The console.log statements Defining myArrowFunc and myArrowFunc defined ran, and we can see the console.log statements from the useEffect hook within TestComponent and TestComponent2 which ran after they were rendered.
Now we click on the button Set to x, which invokes the callback function attached to the onClick event of the button. This callback calls setMessage("x") which updates the state of the App component and consequently triggers a re-render of the component.
In the console window we can see that the App functional component ran (e.g. from the console.log("Defining myArrowFunc)) and one can also see that the useEffect hooks of the child components ran after they were re-rendered.
Now, that TestComponent re-rendered is of course understandable. message state is a prop on TestComponent which would cause a re-render of TestComponent. Also, in the dependency array of TestComponent is specified props (not props.message), but props is a new object on every render (right?) and hence the reference comparison tells us props has changed and so useEffect runs.
That the useEffect hook of TestComponent2 ran can be understood (right?) from the fact that myArrowFunc is re-defined on each render of App, and so the prop passed to TestComponent2 has in fact changed (reference comparison).
Here comes the part where I become confused: the message state in App now holds "x", and so additional clicks to the button will not change the state, and so should not trigger a re-render of App (and any child components dependant). When I click the button the following output happens:
The statements console.log('Defining myArrowfunc'); and console.log('myArrowfunc defined'); ran. What does this mean? Did the component re-render? If the App function ran, then it should have defined a new myArrowFunc (right?), and since TestComponent2 takes that as a prop, it should have re-rendered and run its useEffect hook?
What's interesting as well is that when I again click the button, it does not look like App ran, the output becomes only "Setting message".
Would very much appreciate an outline / explanation of what exactly is going on here.
When you click the button, a console log with setting message will run, and then a setState with will make a re-render.
At the following click, react is clever enough to see that the state you are setting is the same as before so it doesn't re-render
If you change then, the value and click the button, it will re-render, but not for the following cases with the same value.
To sum up, the first's console.log won't run if there's not a re-render, and the procedure of react--if default--memoizing will prevent it

Issue with using useLayoutEffect React

Hook useEffect runs asynchronously and usually after the DOM is rendered/mounted, whereas useLayoutEffect runs synchronously and before the DOM is rendered/mounted.
Using my example useEffect, all works fine and I get desired result (I store the previous value that the user has entered using useRef).
But using my example useLayoutEffect, I don't get the wanted result, cause it just works as useEffect in my case. I reckon that it should firstly update prevName.current = name and only after render causing the same value that person has entered. I think, that in that case both prevName.current and name should be rendered as same values. But that doesn't happen.
Can you please explain why?
Here's codesandbox: Sandbox
Here's the code:
import React, { useLayoutEffect } from "react";
import "./styles.css";
import { useState, useRef, useEffect } from "react";
export default function App() {
const [name, setName] = useState("");
const prevName = useRef(name);
useLayoutEffect(() => {
prevName.current = name;
}, [name]);
return (
<div className="App">
<input
value={name}
onChange={(e) => {
setName(e.target.value);
}}
></input>
<div>Your name is {name}</div>
<div>Your previous name is {prevName.current}</div>
</div>
);
}
Hook useEffect runs asynchronously and usually after the DOM is rendered / mounted, whereas useLayoutEffect runs synchronously and before the DOM is rendered / mounted.
Both of them occur after the changes have been put onto the DOM. What's special about layout effects is that if you set state during the layout effect, the resulting render will be done synchronously. For normal effects, if you set state, it will rerender soon, but not synchronously. If you never set state (as in your example), it's not going to make much difference which one you use.
The most common application of useLayoutEffect is that you want to render once, measure the stuff on the dom, and then render again without the user seeing the temporary stuff you had on the dom.

Infinite Re-rendering of React Functional Component using Axios and useState/useEffect?

I am trying to create a React Functional Component using Typescript that will get data from an API and then send that data to another component, however, I am getting an error of "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."
Please offer any advice!
useEffect(() => {
await axios.get('https://quote-garden.herokuapp.com/api/v3/quotes/random').then((resp) => {
setQuote1(resp.data.data[0]);
});
getQuote();
}, []);
Snippet of Code Causing Error
EDIT:
Here is the entire File where the error is occurring.
import React, { useEffect, useState } from 'react';
import { Row, Col, CardGroup } from 'reactstrap';
import axios from 'axios';
import QuoteCard from './QuoteCard';
const MainQuotes = () => {
const [quote1, setQuote1] = useState();
useEffect(() => {
await axios.get('https://quote-garden.herokuapp.com/api/v3/quotes/random').then((resp) => {
setQuote1(resp.data.data[0]);
});
getQuote();
}, []);
return (
<Row>
<Col>
<CardGroup>
<QuoteCard quoteData={quote1} />
</CardGroup>
</Col>
</Row>
);
};
export default MainQuotes;
Depending on the type of quote1 (I'm assuming it's a string) update to:
const [quote1, setQuote1] = useState('')
useEffect(() => {
function fetchQuote() {
await axios.get('https://quote-garden.herokuapp.com/api/v3/quotes/random')
.then((resp) => {
setQuote1(resp.data.data[0]);
});
}
if (!quote1) {
fetchQuote();
}
}, []);
Because the response from the API is a random quote, you have to handle it based on the value of quote1. This should work because quote1 will only be updated after the component mounts (when the value is an empty string), and the following render won't update state, so useEffect won't loop infinitely.
Before the edit, I assumed the axios request was inside of the getQuote function. I have updated my answer to reflect the code you have posted.
If the API response was static, because the items in the dependency array only cause a re-render if they are changed from their current value, it only causes a re render immediately after the first state update.
Can we get your entire code please? It seems the problem is going to be with your state hook and figuring out exactly what its doing.
My guess as of now is your state hook "setQuote1" is being invoked in a way that causes a re-render, which then would invoke your useEffect() again (as useEffect runs at the end of the render cycle) and thus, calling upon your setQuote1 hook again. This repeats itself indefinitely.
useEffect() allows for dependencies and gives you the power to tell exactly when useEffect() should be invoked.
Example: useEffect(() { code here }, [WATCH SPECIFICALLY THIS AND RUN ONLY WHEN THIS UPDATES]).
When you leave the dependency array empty, you tell the hook to run whenever ANYTHING updates state. This isn't necessarily bad but in some cases you only want your useEffect() to run when a specific state updates.
Something in your application must be triggering your useEffect() hook to run when you aren't wanting it to.

What can cause react error 307 (https://reactjs.org/link/invalid-hook-call)

We have a rather big react application that is occasionally resulting in the following error:
https://reactjs.org/docs/error-decoder.html/?invariant=307
Hooks can only be called inside the body of a function component.
(https://reactjs.org/link/invalid-hook-call)
Sadly that is only happening in the production environment and we can't manage to reproduce the error in our development environment. And even in production it happens rarely, our QA department was also unable to reproduce it...
There is no obvious call of a hook outside of a function component and the component is not called outside of render return of the outer components.
Here is a extremely simplified example of the structure I'm talking about (and also a little bit of the structure of the function inside which the error happens):
import React, {useState, useEffect} from 'react';
class Outer extends React.Component {
render () {
return (
<div>
<Fu outerProps={this.props} />
</div>
);
}
}
const Fu = ({outerProps}) => {
const [open, setOpen] = useState(false);
const [boundCloseFn] = useState(() => setOpen.bind(null, false));
//the 2nd useSetState is only used to ensure a reference to the same function is obtained for every render
//since we only need the initial state the 'bind' happens inside a function, using the 'useState' lazy loading
useEffect(
() => {
document.addEventListener('click', boundCloseFn, true);
document.addEventListener('touchend', boundCloseFn, true);
return () => {
document.removeEventListener('click', boundCloseFn, true);
document.removeEventListener('touchend', boundCloseFn, true);
};
},
[boundCloseFn]
);
const switchOpen = () => {
setOpen(!open);
};
return (
<div onClick={switchOpen}>
{open && <div>Stuff</div>}
<div>Other stuff</div>
</div>
);
};
We realise the creation of the boundCloseFn can be done with different hooks, but we don't think that is causing the issue we have.
The react error seems to happen with the extremely simple first useState, the very first line of the Fu function.
While the entire app has a lot of complicated logic and is loading some components lazily, this particular part is loaded directly, similarly to how it is in the example.
The reason we mix classes and hooks is that the class was written years ago while that component was added recently and we are trying to use hooks from now on.
Our current react version is 16.8.4.

React useState hook - setState causing multiple re-renders [duplicate]

I have this simple bit of code here
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [number, setNumber] = useState(0);
function chaneNumber() {
setNumber(state => state + 1);
}
console.log("here");
return (
<div className="App">
<button onClick={chaneNumber}>Change number</button>
{number}
</div>
);
}
Every time I click the button, I get 2 logs in my console indicating that the component renders twice. I found one post saying this is about strict mode, but I have not enabled strict mode. Why is this component rendering twice on each state update?
Here is a codesandbox link to try it out.
Your App component is rendered within React.StrictMode which is what causes your code to run in strict mode and in strict mode the consoles are shown twice because each function is made to run twice in development mode
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
According to react docs:
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
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer

Categories

Resources