How do I console log the value of initialized useState in react? - reactjs

Initializing values
const initialValues = {
bag: 32,
donor: "",
};
const [values, setValues] = useState(initialValues);
console.log(values.bag)
I don't know more about hooks but when I console.log the value is showing infinite looping value. Am I doing something wrong?

As you are using the functional components, you could set your state or put console.log into the useEffect() hook. Also, you could call your own function in this functional component to do the same. I am writing out both the scenarios in the below code snippet.
import React,{useEffect,useState} from 'react';
export default function IntroToReact(){
const initialValues = {bag: 32,donor:""};
const [values, setValues] = useState(initialValues);
//Effect is called once during init
useEffect(()=>{
console.log(values.bag);
},[]);
//Effect is called everytime the values change
useEffect(()=>{
console.log(values.bag);
},[values]);
// Display the values
function onChangeValues(){
console.log(values.bag)
}
return (
<div>
{onChangeValues()}
</div>
);
}`
For further understanding, this concept you could refer to the official website of the react:- https://reactjs.org/docs/hooks-effect.html
I hope it will help you.
Thank you :)

Hooks does not work inside classes. It works with function based component. It should always be used at the top level of the React Function.
I have run your code it's working fine. But you need to keep in mind some points using hooks first what syntax and approach you are following. Like your code needed to run in function-based component. I am adding a small snippet which is running fine.
import React, {useState} from 'react';
function Home(){
const initialValues = {
bag: 90,
donor: "",
};
const [values, setValues] = useState(initialValues);
console.log(values.bag)
return(
<div>
<h1>Welcome To Home</h1>
</div>
);}export default Home;
Set the indentation properly and call this component into your main component(if it is App.js or App.jsx and index.js or index.jsx)
Follow this official documentation of react.js. I am showing something in this documentation which will help you to know more about hooks.
enter link description here

Related

useState is not changing it's value on useEffect hook [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 months ago.
I am trying to animate a header on a personal site I'm working on.
It's a simple fade in fade out effect as the user scrolls through the page.
To get this effect what i'm doing is using an Intersection Observer to check wether or not a certain element is in view or not.
When that element is in view, i'd like to change the state elementInView to reflect that or not using a boolean value.
when I console.log the isIntersecting value of the element that the observer is listening to, I can see that it changes True or False based on whether the element is in view or not.
so... since that is working, i'm using that value to set state of elementInView.
BUT!!! the state is not changing when the element is in view, even though it should. I'm not sure what went wrong but I cannot figure it out for the life of me.
here's the code:
import React from 'react';
import { useEffect, useRef, useState } from 'react';
import { Link, useRouteMatch, useNavigate } from 'react-router-dom';
import BoulderImage from "../data/nat_pic_boulders_and_tree.jpeg";
import FenceImage from "../data/nat_pic_fence.jpeg";
import GrassField from '../data/nat_pic_grass.jpeg';
export default function HomePage() {
const navigate = useNavigate();
const firstRef = useRef();
const secondRef = useRef();
const [elementInView, setElementInView] = useState();
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
setElementInView(entry.isInteresecting);
console.log(elementInView);
})
observer.observe(firstRef.current);
observer.observe(secondRef.current);
}, [elementInView])
return (
<div>
<h1 className="main-intro-header">Welcome Home</h1>
<div className='nav-links'>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
<div className="stop-point-one" ref={firstRef}>stop point</div>
<div className="stop-point-two" ref={secondRef}>stop point</div>
</div>
)}
the list at the end of the useEffect hook. i've tried putting elementInView in there and taking it out and re-running it. no luck.
I think that the thing you are missing is that after calling setElementInView it does not change elementInView on the current render. Only in the next render for that component elementInView will get the new state. And that way you are getting the wrong output to the console.
The way to solve it is to add another useEffect hook with elementInView as a dependency and do you logic there.
You can watch a great video of Jack Herrington regarding this:
https://www.youtube.com/watch?v=RAJD4KpX8LA
Henrys comment links to a better explanation than I can give. Summed up, since setState is performed in an async nature, the value will not be what you set it to immediately after the setState line is ran. If you move your console.log line out of your observer declaration, into the body of the useEffect, it should work as you expect. I put together a little sandbox and spaced some elements apart to put them off screen to show this as well.
https://codesandbox.io/s/observer-test-m5qh88?file=/src/App.js
If you scroll the elements on and off the page with the console open, you'll see it update each time they move off and back onto the screen.
I figured it out!
I saw a simpler way to set the state of these elements as they appear on the screen.
i updated this piece of code:
const [elementInView, setElementInView] = useState();
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
setElementInView(entry.isInteresecting);
console.log(elementInView);
})
observer.observe(firstRef.current);
observer.observe(secondRef.current);
}, [elementInView])
to this:
import { useInView } from 'react-intersection-observer';
const { ref: firstRef, inView: firstBreakIsVisibile} = useInView();
const { ref: secondRef, inView: secondBreakIsVisibile} = useInView();
I used npm to install the react-intersection-observer dependency.

State variable is changing when accessed from useEffect()

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")
);

React and useState(): multiple re-renders if initial state is a string

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

Adding an object to useState in react. Error "Too many re-renders"

As soon as I add an object to "setTitle" I get an error. Setting the object in "useState()" works.
import React, { useState } from "react";
export default function App() {
const [title, setTitle] = useState({});
setTitle({
"somthing": "dfgsf"
});
return <p>df</p>;
}
Too many re-renders. React limits the number of renders to prevent an
infinite loop.
Live: https://codesandbox.io/s/custom-hook-playground-bzt6s?file=/src/App.js
When component will mount for the first time, "setTitle" function will be called which will update the state.
When state updates, re-rendering happens, thus "setTitle" function will be called again, triggering an infinite loop
Solution:
Use "useffect" or some other function to update the state
You must use initialState when defining the state. You can then add an onClickin order to change the title state using setState
This is the right way:
import React, { useState } from "react";
export default function App() {
const [title, setTitle] = useState({"somthing": "dfgsf"});
function buttonHandler() {
setTitle({
"somthing": "dfgsf"
});
console.log(title)
}
return <button onClick={buttonHandler}>sdf</button>;
}
This is happening because state updates cause a re-render. Because you're updating the state unconditionally the component keeps re-rendering indefinitely.
This doesn't have anything to do with initial state.
useState takes an initialState as it's argument.
Try something like this?
export default function App() {
const [title, setTitle] = useState("My Initial Title");
return <p onClick={() => setTitle('New')}>{title}</p>;
}

Set state when testing functional component with useState() hook

When I tested class component with enzyme I could do wrapper.setState({}) to set state. How can I do the same now, when I am testing function component with useState() hook?
For example in my component I have:
const [mode, setMode] = useState("my value");
And I want to change mode inside my test
When using state from hooks, your test must ignore implementation details like state in order to properly test it.
You can still make sure the component passes the correct state into its children.
You can find a great example in this blog post written by Kent C. Dodds.
Here's an excerpt from it with a code example.
Test that relies on state implementation details -
test('setOpenIndex sets the open index state properly', () => {
const wrapper = mount(<Accordion items={[]} />)
expect(wrapper.state('openIndex')).toBe(0)
wrapper.instance().setOpenIndex(1)
expect(wrapper.state('openIndex')).toBe(1)
})
Test that does not rely on state implementation details -
test('counter increments the count', () => {
const {container} = render(<Counter />)
const button = container.firstChild
expect(button.textContent).toBe('0')
fireEvent.click(button)
expect(button.textContent).toBe('1')
})
This is the way that I found to do it, I'm not saying this is right or wrong. In my case, a block of code was dependent on state being set to a particular value. I will keep my opinions about testing in React to myself.
In your test file:
Adjust your import for the react library
import * as React from 'react'
Then in your test spy on useState and mock its implementation
const stateSetter = jest.fn()
jest
.spyOn(React, 'useState')
//Simulate that mode state value was set to 'new mode value'
.mockImplementation(stateValue => [stateValue='new mode value', stateSetter])
Please be aware that mocking useState this will way apply to all instances where useState is called for your test, so if you have more than one state value that you are looking at, they will all be set to 'new mode value'. Someone else may be able to help you sort that out. Hope it helps.
At top of test file, can be defined first as:
import { useState } from 'react';
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: jest.fn()
}));
const useStateMock: jest.Mock<typeof useState> = useState as never;
After that at each test can be used with different value which is wanted to be tested:
const setValue = jest.fn();
useStateMock
.mockImplementation(() => ['value', setValue]);

Resources