Why does react re-render twice when using the state hook (useState)? - reactjs

I have created a component, App, based on reactjs's example of the state hook useState. Here is my code:
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
console.log("before");
const [count, setCount] = useState(0);
console.log("after");
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count+1)}>
Click me
</button>
</div>
);
}
export default App;
When I run this using node, App renders a button on http://localhost:3000/:
Based on the explanation accompanying the reactjs example (link above), I would expect that clicking the button would cause App to be re-rendered one time. However, clicking (once) actually causes the re-render to occur twice:
Why does this example lead to the re-render occurring twice?
How can I correct this so that does not occur more than necessary?
Note: With each click, two new lines of each 'before' and 'after' are printed to the console, so the double-rendering appears to occur with each click, not due to the initial render.

What you're seeing is the first 'before' and 'after' is from the initial render and the second pair is from when you click the button and state is updated.
It seems the offending code comes from <React.StrictMode>
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
Removing the <React.StrictMode> gets rid of this issue.
ReactDOM.render(<App />, document.getElementById('root'));
Further Reading: React Components rendered twice — any way to fix this?

Related

React useState handler called TWICE

Having such a simple React App:
import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
console.log("App")
const [inputValue, setInputValue] = useState(0);
return (
<>
<input
type="text"
onChange={(e) => setInputValue("e.target.value")}
/>
<button onClick={(e) => setInputValue(1)}>
Click me
</button>
<h1>Render Count: {inputValue}</h1>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I'm getting initially (after the App loads):
App
AND
App
App
on interaction - when I click the button or type sth. in the input field.
My question is WHY does the App is printed TWICE in the second case (interaction)!? It's strange because the inputValue is ONLY changed ONCE on the first click/input but in the second interaction it stays the same, not changing anymore!
P.S.
It's NOT the case of the react strict mode! - In the index.js I have:
...
ReactDOM.render(<App />, rootElement);
...
so I'm NOT using react strict mode!
When the component mounts, there is always one render.
Then when the state changes from 0 -> 1 there is another render.
When using the useState hook, it re-renders whenever a state value changes, not necessarily every time you call setState (which was the old behaviour). So every time you click after the first time, the state goes from 1 -> 1 and you get no re render because the value is the same.
React setState causes re-render.
You can learn more about this behavior here: How does React.useState triggers re-render?

Any way to open a modal dialog without explicitly storing state?

I have been using react-modal recently, and the pattern to show a modal is something like:
const [modalIsOpen, setModalIsOpen] = useState(false);
return (<ModalDialog isOpen={modalIsOpen}><div>some content</div></ModalDialog>)
...which feels very React-y, but overly verbose. Is there a way to enable something where I can - within a click handler - simply call:
showModal(MyComponent)
and have the state be handled automatically? I've thought about creating a Context around the app that allows showModal to grab state associated with MyComponent, in which case this would itself be a hook, so it would have to follow certain rules (i.e., no conditional calling), so I'm not sure I could make it work as cleanly as I'd like.
Is there a way to do this within the React ecosystem? Or should I just give up and use the existing mechanism?
Yes! You can do it by using the "render" function.
Unfortunately it doesn't work in CodeSandBox, but you can fork it from here:
https://github.com/BHVampire/modal
You close the modal by clicking outside.
This is how I did it:
At the end you'll be able to open a modal like this from any part of your application:
createModal(<h1>Success</h1>)
First, you add a container for your modals outside the App component, so you'll never have problems with the z-index:
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.scss'
import App from './App.jsx'
ReactDOM.render(
<React.StrictMode>
<div id="modal-container"></div> <- This is the container
<App />
</React.StrictMode>,
document.getElementById('root')
)
app.jsx
import createModal from "./components/Modal"
const App = () => {
return <button onClick={() => createModal(<h1>Success</h1>)}>Open Modal</button>
}
export default App
Then I have 2 main files for the modal inside a Modal folder + the style.
Modal/index.js
The state hook is unavoidable for the modal, but you'll never see it again.
import { Fragment, useState } from 'react'
import './Modal.scss'
const Modal = ({ content }) => {
const [isOpen, setIsOpen] = useState(true)
return isOpen
? <Fragment>
<div onClick={() => setIsOpen(false)} className="modal-background" />
<div className="modal" >
{content}
</div>
</Fragment>
: ''
}
export default Modal

Why my simple react component print console twice?

I made a simple components that number state 'a' is increased when the button is clicked.
and I wrote console.log() inside component to check when it is rendered. I expected the console.log is executed once when the button is clicked because component's state is changed.
But, I was wrong and console.log() is executed twice.
Is something wrong? or Is it correct? What i missed?
here is my code
A.jsx
import React, {useState} from 'react';
const A = () => {
const [a, setA] = useState(0);
const onClick = () => setA(a + 1);
console.log('render')
return (
<div>
<p>a : { a}</p>
<button onClick = {onClick}>button</button>
</div>
)
}
export default A;
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import A from './components/A';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<A />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
This app is created by CRA with typescript .
That's all.
Thanks.
****PLUS******
I checked React dev tools Profiler to check the component is really rendered twice when a button is clicked and state is changed. It show me result below
I think there was only one render. If the component was really rendered once, why the console.log exected twice?
I think this is because of React.StrictMode this only happens in development. If you remove React.StrictMode you will get only 1 log.
For more details, check this thread on react repo:
https://github.com/facebook/react/issues/15074
On reading further I found this on React docs as well: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
Hope this helps!

My components are not being rendered when I click a link that should load them

I'm confused as to why nothing happens when I'm clicking links in my app.
In my index.js file, I am loading my main screen called 'Game'. Inside 'Game', I have two links, that when clicked, should render another screen.
In my index.js:
import React from "react";
import ReactDOM from "react-dom";
import Game from "./Game/Game";
ReactDOM.render(
<React.Fragment>
<Game/>
</React.Fragment>,
document.getElementById('gameContainer')
)
In my index.html:
<div>
<div id="gameContainer"></div>
</div>
<div id="root"></div>
My Game.js:
import React from "react";
import CharacterStats from "../CharacterStats";
import DungeonStats from "../DungeonStats";
const characterStatsComponent = () => {
return (
<CharacterStats />
);
}
const dungeonStatsComponent = () => {
return (
<DungeonStats />
);
}
const Game = () => (
<div>
<a id="showCharacter" href="#" onClick={characterStatsComponent}>Show your character</a>
</div>
<br />
<div>
<a id="showDungeon" href="#" onClick={dungeonStatsComponent}>Show current dungeon</a>
</div>
);
export default Game;
The two other components, CharacterStats and DungeonStats are just a few bits of html and reactjs to show some data.
Neither CharacterStats or DungeonStats are loading when I'm clicking the links.
I am also getting no errors in the console.
Nothing happens when the links are clicked.
I also put this inside each onClick event:
console.log('link was clicked');
And it does show the message in the console. So that shows that it knows the link is being clicked.
Is there anything that would prevent them from being loaded?
Thanks!
It wont work because you are returning jsx into the onClick function context, and not into the Game component's return value.
You could define a state using useState, something like showDungeon and showCharacter that defaults to false, change it to true onClick, and in the Game component's return value add:
{ showDungeon && <DungeonStats /> }
React uses something called Synthetic Events to achieve cross browser event handling. If I understood your question correctly than changing the onclick to onClick should do the job for you.

Why does the page flash all green (in react Rendering chrome addon) when changing anything in redux store

I've created a simple app to test what part of the document gets rerendered when I add items to an array and then use .map in react. To manage the state I use redux. To check what gets rerendered I use the react chrome addon with the option Paint flashing selected.
So I expect that when I dispatch an action from a component that modifies the store, only the components connected to that part of the store would flash green. Instead, the whole screen flashes green followed by every single component that will also flash green.
Seems like anything under <Provider /> will just update on any change inside redux store.
I've already tried PureComponent, managing shouldComponentUpdate, React.memo for the function component.
My index file looks like:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./store/reducers";
import "./index.css";
import App from "./App";
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
And my App file:
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import ListComp from "./components/ListComp";
import ListFunc from "./components/ListFunc";
import ButtonMore from "./components/ButtonMore";
export class App extends Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<ButtonMore />
<ListComp />
<ListFunc />
</div>
);
}
}
export default App;
ButtonMore will add items to the store when clicked. It has the action connected so it can dispatch it.
ListComp is connected to the list of items in the store and will .map them. In this case, the main purpose was to test the key property and see if only the new items would flash green.
ListFunc Will do the same as the one above but as a function component.
I wanted to drive this test since in the project I work on we are going crazy with performance issues now that the app is huge. We are thinking of moving away from redux but I don't think this option is good at all.
I expected some green flashes just on the new items displayed. But instead the whole screen will always flash when I change anything in the store.
EDIT: Let me add the example that shows the list of items from the store. I expected this to flash only the new items but instead it flashes the whole component:
import React from "react";
import { connect } from "react-redux";
const ListFunc = props => {
return (
<ul className="ListComp">
{props.listItems.map((item, i) => {
return <li key={`itemFunc_${i}`}>{item}</li>;
})}
</ul>
);
};
const mapStateToProps = state => {
return { listItems: state.reducer };
};
export default connect(
mapStateToProps,
null
)(ListFunc);
React-Redux v6 changed the internal implementation in several ways. As part of that, the connect() wrapper components do actually re-render when an action is dispatched, even when your components don't.
For a variety of reasons, we're changing that behavior as part of v7, which is now available as a beta.
update
After looking at the code snippet you've posted: yes, I would still expect the example you've shown to cause both the list items and the list to re-render. I can't say 100% for sure because you haven't shown your reducer, but assuming that one of the list items is updated properly, state.reducer should be a new array reference as well. That will cause ListFunc to re-render.

Resources