When I render the App component the code inside setTimeout runs twice.
I saw that setTimeout runs after the call stack has finished executing. I don't know whether its relevant to this.
Is it something to do with how react renders components.
index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
rootElement
);
App.js
import "./styles.css";
export default function App() {
setTimeout(() => {
console.log(`Logged!`);
},
1000
)
return (
<div className="App">
<h1>Hello</h1>
</div>
);
}
Your setTimeOut is called on every render of the App component.
In your case, React.StrictMode causes the re-renders.
To prevent this, you can add it in a useEffect hook with an empty dependency array:
useEffect(() => {
const timer = setTimeout(() => {
console.log(`Logged!`);
}, 1000);
return () => clearTimeout(timer);
}, []);
Related
I have developed small react application and run in different browsers.
NOTE: I never used StrictMode
Chrome browser render 3 times
firefox browser render 2 times
Brave browser render 2 times
Code,
import { useEffect, useReducer, useRef } from "react";
function TestComponent() {
const [renderId, forceUpdate] = useReducer((x) => x + 1, 0);
let renderCount = useRef(0);
renderCount.current = renderCount.current + 1;
console.info("*** Rnder Called *** " + renderCount.current);
useEffect(() => {
setTimeout(() => {
forceUpdate();
}, 2000);
}, [forceUpdate]);
return (
<>
<p>Welcome</p>
</>
);
}
export default TestComponent;
App.tsx file,
import "./App.css";
import TestComponent from "./UseReducerComponent/test";
export default function App() {
console.log("APP CALLED ####");
return (
<div className="parent_div">
<h1>React Project</h1>
<TestComponent />
</div>
);
}
index.tsx file,
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(<App />);
reportWebVitals();
In Chrome browser,
In Firefox and Brave browser respectively
Anyone explain why it's happen
Thank you.
How do I achieve the code below using Next.js.
I believe there is an issue with Next.js not being able to access the window object unless you're inside a useEffect(() => {}) hook.
Changing to regular React, this code worked fine.
What am I missing in the Next.js ecosystem that doesn't allow this type of delayed render to work?
Thank you.
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { initializeContract } from "./utils/near";
window.nearInitPromise = initializeContract()
.then(() => {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
})
.catch(console.error);
reportWebVitals();
Update: Here is my attempt with Next.js
import { useEffect } from "react";
import { initializeContract } from "../utils/near";
function MyApp({ Component, pageProps }) {
useEffect(() => {
window.nearInitPromise = initializeContract().then(() => renderApp());
}, []);
const renderApp = () => {
return <Component {...pageProps} />;
};
}
export default MyApp;
That's because window object is not available on server-side (nodejs), when you use it inside useHook it's executed client-side. (when the component is mounted).
So if you run your code inside an useEfect this ensures that your code only runs on the client-side.
Window is not defined in Next.js React app
import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
function AppTwo(props){
const [state, setState] = React.useState("initial value");
useEffect(() => {
console.log(`useEffect: ${state}`);
},[state]);
setState("updated value");
console.log(state);
return `<h1>${state}</h1>`;
}
ReactDOM.render(
<>
<AppTwo />
</>,
document.getElementById('root')
);
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
Don't call setState inside of the component body. It will update every time the component re-renders. That's why you are getting an infinite loop of state updates.
WORKS!
import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
function AppTwo(props){
const [state, setState] = React.useState("initial value");
useEffect(() => {
console.log(`useEffect: ${state}`);
// document.getElementById('btn').innerHTML = `<h1>State: ${state}</h1>`;
},[state]);
console.log(state);
return (
<>
<h1>State: {state}</h1>
<button id="btn" onClick={() => {setState('updated value')}}>
click
</button>
</>
)
}
ReactDOM.render(
<>
<AppTwo />
</>,
document.getElementById('root')
);
I am trying to make a React project which fetches data from an API, and I'm trying to use useReducer hook with useContext from an external file.
Folder Structure:
public
src (and in src folder, I have context.js and reducer.js along with rest of the components and and other files)
package.json
reducer.js:
const reducer = (currentState, action)=>{
switch(action.type){
case 'setBreedList':
// console.log('setBreedList ran')
return {...currentState, breedList: [...currentState.breedList, action.payload]}
// return currentState
// return {...currentState.breedList, action.payload}
default:
return currentState
}
}
export default reducer
context.js
import React, {useContext, useReducer} from 'react';
import reducer from './reducer';
const AppContext = React.createContext();
const initalState = {
breedList: [],
imageList: []
}
const AppProvider = ({children})=>{
const [state, dispatch] = useReducer(reducer, initalState);
const fetchAllBreeds = ()=>{
fetch('https://dog.ceo/api/breeds/list/all')
.then(res=>res.json())
.then(data=>dispatch({type: 'setBreedList', payload: data}))
}
return (
<AppContext.Provider value={{...state, fetchAllBreeds}}>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = ()=>{
return useContext(AppContext)
}
export {AppContext, AppProvider}
and this is index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {AppProvider} from './utils/context';
ReactDOM.render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>,
document.getElementById('root')
);
and finally, this is Input.jsx, where I call the fetchAllBreeds function:
import React from 'react'
import {useGlobalContext} from '../utils/context'
const Input = () => {
const {state, fetchAllBreeds} = useGlobalContext();
fetchAllBreeds();
console.log('hello')
return (
<div>
<p>hello</p>
</div>
)
}
export default Input
Now, when I try to call the fetchAllBreeds function in the Input.jsx file above, it prints on the console, 'hello', endlessly. I can't figure out why this is happening and I have tried some different things, but the result is the same. Will be grateful to your help.
Input component is calling fetchAllBreeds function, that will update the context value which in turn will re-render Input component, hence fetchAllBreeds will re-run. Don't call any method inside the component body which will cause re-renders.
Use useEffect hook inside Input component. which will execute only once.
import React, {useEffect} from 'react'
import {useGlobalContext} from '../utils/context'
const Input = () => {
const {state, fetchAllBreeds} = useGlobalContext();
useEffect(()=>{
fetchAllBreeds();
}, [])
return (
<div>
<p>hello</p>
</div>
)
}
export default Input
So I'm trying to do error handling using a library called react-alert.
I'm using Axios as my REST handler. I created an action and a reducer, which are both working fine; saw my state update in my DevTools extension when I make a post request.
Here's my functional component called Alert.js
import React, { Fragment, useState } from "react";
import { useAlert } from "react-alert";
import { useSelector } from "react-redux";
import { useEffect } from "react";
export default function Alert(props) {
const alert = useAlert();
const error = useSelector((state) => state.errors.errors);
const [errorMSG, setERRORS] = useState();
useEffect(() => {
if (error !== errorMSG) {
setERRORS(error);
alert.show(errorMSG);
} else {
alert.show("Welcome!");
}
}, [errorMSG]);
return <Fragment />;
then I called it in my main App.js
//Main Imports
import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";
// React Alert
import { Provider as AlertProvider } from "react-alert";
import AlertTemplate from "react-alert-template-basic";
import Alert from "./layout/Alert";
const alertOptions = {
timeout: 3000,
position: "top center",
};
//Components import
import Header from "./layout/Header";
import Dashboard from "./products/Dashboard";
//Redux import
import { Provider } from "react-redux";
import store from "../store";
class App extends Component {
render() {
return (
<Provider store={store}>
<AlertProvider template={AlertTemplate} {...alertOptions}>
<Alert />
<Fragment>
<Header />
<div className="container-fluid">
<Dashboard />
</div>
</Fragment>
</AlertProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
I do see the Welcome! when the app mounts, but when I have an error I see nothing. Could anyone help me find a solution to this; would be really appreciated.
You see "Welcome" because when your component mounts first time, it calls the useEffect hook. But the useEffect hook isn't getting triggered afterwards because of the wrong dependency item provided for the useEffect. You need to add error instead of errorMSG, because error is coming from your store and you want to show the message when there is a new error emitted:
useEffect(() => {
if (error !== errorMSG) {
setERRORS(error);
alert.show(errorMSG);
} else {
alert.show("Welcome!");
}
}, [error]);