In whichever site I visit to get my doubt clear about state in react I always found this defination in common which is:
"an object of a set of observable properties that control the behavior of the component". And I still don't understand the state in react. Consider an example below
import React,{useState} from 'react';
export const City = ()=>{
const [altitude,setAltitude] = useState("");
const getAltitude=()=>{
navigator.geolocation.watchPosition((position)=>{
const alt = {
lat:position.coords.latitude,
long:position.coords.longitude
}
setAltitude(alt);
console.log(altitude);
})
}
return(
<div id="location">
{getAltitude()}
<h3>This is location</h3>
</div>
)
}
But the above program can also be written without using state as shown below
import React,{useState} from 'react';
export const City = ()=>{
let lat;
let long;
const getAltitude=()=>{
navigator.geolocation.watchPosition((position)=>{
lat = position.coords.latitude;
long = position.coords.longitude;
})
console.log(lat,long);
}
return(
<div id="location">
{getAltitude()}
<h3>This is location</h3>
</div>
)
}
If we can write in this way too then what is the use of state in react.
If I'm wrong I request you to explain me in detail. I'm not able to sleep unless this doubt doesn't get clear.
For the understanding purpose I've created these two snippets, one using state variable and the other using regular js variables.
Using state variable
const { useState } = React;
const Counter = () => {
const [count, setCount] = useState(0);
const onClick = () => {
//Update the state
setCount(c => c + 1);
}
return (
<div>
Count: {count}
<div>
<button onClick={onClick}>Increment</button>
</div>
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Using regular variable
const { useState } = React;
const Counter = () => {
let count = 0;
const onClick = () => {
//Update the variable
count += 1;
console.log(count);
}
return (
<div>
Count: {count}
<div>
<button onClick={onClick}>Increment</button>
</div>
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
In both of the above scenarios we are updating the count on click of the button, but then in scenario 1, the updated value is displayed on the DOM where as in scenario 2, it's not.
So, basically when you want to re-render the component on change of the variable we keep those variables in the state. React will be able to understand the state changes and update the DOM accordingly
As stated correctly in your citation, React maintains a state that is used to to figure out, beside some other features, when to re-render your component. In your examples, it seems like you wanted to add an event listener for watchPosition, and show the calculated values in your City component. If I am correct please consider this example:
import React,{useState} from 'react';
export const City = ()=>{
const [altitude,setAltitude] = useState({});
const calculateAltitude=()=>{
navigator.geolocation.watchPosition((position)=>{
const alt = {
lat:position.coords.latitude,
long:position.coords.longitude
}
setAltitude(alt); // you set the state here
})
}
calculateAltitude(); // This function is called on each render. It's better to put it into a "componentDidMount" equivalent (second example)
return(
<div id="location">
<h3>This is the location</h3>
<div>Lat: {altitude.lat}</div> {/*You use the state here*/}
<div>Lat: {altitude.long}</div>
</div>
)
}
Each time watchPosition is executed, your state altitude is updated and the component will re-render. This means, that the render function is executed and the current state of altitude is used to display the latitude and longitude. In the first example, calculateAltitude() will be executed each time, your component renders. Since that is not best practice, you should put that call into a useEffect hook, with an empty-array dependency. It is an equivalent to the componentDidMount() fucntion for React class components (React.Component). You can find a good explanation here.
So, in order to have a clean code, you should use this component:
import React,{useState, useEffect} from 'react';
export const City = ()=>{
const [altitude,setAltitude] = useState({});
const calculateAltitude=()=>{
navigator.geolocation.watchPosition((position)=>{ // each time this geolocation lib calls your function, your state will be updated.
const alt = {
lat:position.coords.latitude,
long:position.coords.longitude
}
setAltitude(alt); // you set the state here
})
}
useEffect(() =>
{
calculateAltitude() // register your event listener once.
},[]) // on Component mount. Executed once.
return(
<div id="location">
<h3>This is the location</h3>
<div>Lat: {altitude.lat}</div> {/*You use the state here. It is updated each time your geolocation lib calls your listener.*/}
<div>Lat: {altitude.long}</div>
</div>
)
}
I recommend to read carefully about the React states. Here are some good links:
offical doc
article about react states in function
components
One of the features that is at the fundament of React is that it allows you to update your UI, or, in other terms, the HTML output of your app, after a change to a variable. In React, state is a way to declare the variables that, depending on your business logic, can ultimately update the UI. React does this by keeping track of the state and rerendering parts of the HTML that need the update.
In your example, you made altitude part of your state, but your logic does not update the UI anywhere—it just determines a geolocation on first load. If this is what you want, that's fine, you won't need state for that. If, however, you want to make updates to your HTML, e.g. if you'd like to show the realtime location of a user on a map, you could make use of React's state: you write location updates to the state, and will need some custom logic that handles what happens if the altitude value has changed. You could write this custom logic as a side effect.
state variables will be accessible into jsx ( i mean in rendered part of the component), so in your example, altitude will be accessible in the html part, but if you didn't assign it to state variable wont be able to access it
return(
<div id="location">
<h3>This is location {altitude.alt}</h3>
</div>
)
Related
Child component is passing data as prop via function to Parent. Inside Parent I have a variable with a string value. I also have a useState. The initial value of that useState should be the string value.
But for some reason setting the initial value to the variable doesn't render the state initial value.
const header = () => {
//some data I fetched
const [address, setAddress] = useState("");
//take the string I need
let city = address.split(", ")[0];
//initiate the useState with the string above
const [updateAddress, setUpdateAddress] = useState(city);
//accept new location from SearchLocation component and pass it to the address
const handleChangeLocation = (userInput) => {
setUpdateAddress(userInput);
};
//won't render the value of updateAddress which is the city I passed
return (<div className={styles.city}>{updateAddress}</div>
<SearchLocation changeLocation={handleChangeLocation} />)
}
When I add a primitive value to the city variable or the useState for testing purposes it won't render either.
If I go to the child component(SearchLocation) that transfers the data I need to the parent component and, I remove the prop the city variable in the parent component, the city value will get through fine.
I would appreciate some explanation. Thank you
--
Below is how I did it based on what you said you needed.
You update a state directly in the root of the component since it's asynchronous, you have to update them inside a useEffect hook. Especially if the initial data is fetched from an API.
Your code below wouldn't work.
....
//take the string I need
let city = address.split(", ")[0];
//initiate the useState with the string above
const [updateAddress, setUpdateAddress] = useState(city);
...
Anyway here's what I did, so based from your code an address is in the format of city, xxxx, xxxx, so you need to get the [0] index.
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom'
// make sure your component starts with a capital letter
const Header = () => {
// assign the search keyword to a different state
const [searchKeyword, setSearchKeyword] = useState('')
// then the specific data you want
const [addressCity, setAddressCity] = useState('')
// Setting the initial address from the API
// You need to put updating of the state inside a useEffect or what because it is asynchronous
useEffect(() => {
// fetch from the api
// assign the fetched data to the address state
const addressFromAPI = 'Test, address'
setAddressCity(addressFromAPI.split(', ')[0])
}, [])
// Add a onChange handler for the search input
const handleSearch = (e) => {
// assign the input of the user to the search keyword state
const userInput = e.target.value
setSearchKeyword(userInput)
// now get what you wanted, the city
const city = userInput.split(', ')[0]
console.log(city)
setAddressCity(city)
}
// now everything works
return <>
<div>Search keyword: {searchKeyword}</div>
<div>City: {addressCity}</div>
<input onChange={handleSearch} value={searchKeyword} />
</>
}
ReactDOM.render(
<Header />,
document.getElementById('root'),
)
here's a screenshot
I'm not sure if this is what you wanted to achieve, please let me know if it is or give us more info about your problem:)
Btw it seems like you are in the early stage of developing in react, reminds me of the days. Maybe you could try reading the docs or this free course from scrimba
Let's say I have a React component somewhat like this:
function App() {
const [step, setStep] = useState(0);
const [someValue, setSomeValue] = useState("xyz");
const var1 = "abc";
const var2 = "def";
function StepZero() {
return <div>
<p>{someValue}</p>
<input type="text" id="fname" name="fname" />
<h1>{var1} {var2}</h1>
<Button onClick={() => setSomeValue("123")}>Click</Button>
</div>;
}
function StepOne() {
return <div>
<h1>{someValue}</h1>
<Button onClick={() => setSomeValue("456")}>Click</Button>
<h2>{var1}</h2>
<h3>{var2}</h3>
</div>;
}
return (
<div>
{step === 0 ? (
<StepZero />
) : (
<StepOne />
)}
</div>
);
}
What happens here is, once someValue gets set, the whole StepZero gets re-rendered and the input lost. Really any user interaction gets reset, e.g. an accordion that got opened.
What resolves this is putting StepZero and StepOne directly into the return function, or putting them outside the App component and then passing all the variables in the params like:
{ var1, var2, someValue, setSomeValue }
Is there a better way to do this that I can separate the two steps into components while still being able to access the states and variables/constants, without state changes re-rendering the components every time, or without having to pass all the required variables as parameters?
Note this example app isn't meant to do anything functionally useful
Based on react architecture passing states and variables through props is the best solution.
Now, based on what you requested I have multiple suggestion:
if the constants are resetting, have you tried to save the constants in a ref? (for ex: const var1 = useRef("abc");)
if you don't want to pass data as props but you want them to share the same variables, you can either add Redux (React-redux) or use React Context (How to use React Context)
But if you're project isn't big, i'd suggest you pass them as props, similar to the following:
In the parent App component:
const [step, setStep] = useState(0);
const [someValue, setSomeValue] = useState("xyz");
const var1 = useRef("abc");
const var2 = useRef("def");
And in a separate component
function StepZero({someValue,var1,var2,setSomeValue}) {
return <div>
<p>{someValue}</p>
<input type="text" id="fname" name="fname" />
<h1>{var1.current} {var2.current}</h1>
<Button onClick={() => setSomeValue("123")}>Click</Button>
</div>;
}
First all your child components have to be outside your App function, in order of having their own state.
Secondly you will pass the somevalue state to all your child in a props and lift the state up to your parent with a callback function.
https://reactjs.org/docs/lifting-state-up.html
Something like this (I simplified your code for the example)
function StepZero({val,var1,var2,callback}) {
return <div>
<p>{val}</p>
<input type="text" id="fname" name="fname" />
<h1>{var1} {var2}</h1>
<Button onClick={() => callback("123")}>Click</Button>
</div>;
}
function App() {
const [step, setStep] = useState(0);
const [someValue, setSomeValue] = useState("xyz");
const var1 = "abc";
const var2 = "def";
return (
<div>
<StepZero val={someValue} var1={var1} var2={var2} callback={setSomeValue} />
</div>
);
}
I think you need to know more about how reactjs its work, and base on that you can go...the main idea you need start think about that react is Component-Based, and thats mean "Build encapsulated components that manage their own state"
so, from this point you can know that we have component which its has a control to manage their state, so this will move to next step..
At first level, we have props, state and context, which all of these concepts will help you to pass/share data
1- Props will used to pass data from higher component to nested component, and its immutable
2- State will track your value change and you can updated it to update needed functions...
3- Context which its used to share data between multiple component without passing a props from higher one to lowest one...
After we know about these details, we need to go to step two, which we need to understand Re-render...
The render spitted to initial render which happens when a component first appears on the screen and re-render happens when React needs to update the app with some new data, and usually its happen base on action or interactive action...
Note: react handle the re-render and know which spesfic part is render and handling to re-render it, its virtual dom and can be controlled very well...
Now lets go to your code:
1- You can pass your details as props, and you can split App component to small component:
function StepZero(props) {
return <div>
<p>{props.someValue}</p>
<input type="text" id="fname" name="fname" />
<h1>{var1} {var2}</h1>
<Button onClick={() => props.setSomeValue("123")}>Click</Button>
</div>;
}
function StepOne(props) {
return <div>
<h1>{props.someValue}</h1>
<Button onClick={() => props.setSomeValue("456")}>Click</Button>
<h2>{var1}</h2>
<h3>{var2}</h3>
</div>;
}
function App() {
const [step, setStep] = useState(0);
const [someValue, setSomeValue] = useState("xyz");
const var1 = "abc";
const var2 = "def";
return (
<div>
{step === 0 ? (
<StepZero {youProps}/>
) : (
<StepOne {youProps}/>
)}
</div>
);
}
The second solution is use content React Context
For re-render you need to handling update dependency correctly and if its in loop or list you need to add key
Also, you must know that var1 and var2 will keep re-render since its set direct without state, ref and its put directly in component not a static / const out side the component, so each time you re-render the component its will has a new memory address which its mean a new re-render for any place has lisen to it...
This is code of my AddBlog Component
import React, { useState } from "react";
function AddBlogs() {
const[state, setState] = useState({blogs:[]})
const AddAnother = () => {
return (
<div>
<label>Topic</label>
<input></input>
<button onClick={addblog}>Add another topic</button>
</div>
);
}
const addblog = () =>{
setState({
blogs:[...state.blogs,<AddAnother></AddAnother>]
});
console.log("Hello");
}
return (
<div>
<form>
<div>
<label>Topic</label>
<input></input>
<button onClick={addblog}>Add another topic</button>
</div>
<div>
{state.blogs}
</div>
</form>
</div>
);
}
export default AddBlogs;
When I click that Add another topic button AddAnother components blinks for just 0.5 second or less. Any solution for this?
I see a couple things that will cause problems. First, when you update state, you shouldn't use the current state. Instead, you should use the setState that accepts a function with the old state as the first parameter, such as the following:
const addblog = () => {
setState((oldState) => {
return { blogs: [...oldState.blogs, <AddAnother />] };
});
console.log("Hello");
};
This won't solve your problem, though. The issue you're seeing is due to you not having a key in your array of components. So try this:
const addblog = () => {
setState((oldState) => {
return { blogs: [...oldState.blogs, <AddAnother key={oldState.blogs.length} />] };
});
console.log("Hello");
};
As David pointed out, the form is also posting, which is causing part of the problem as well.
Because the <form> is posting. If you don't need this to be an actual <form> then remove that element entirely. But if for some reason you want to keep that element, change the button type(s):
<button type="button" onClick={addblog}>Add another topic</button>
By default a <button> is of type submit unless otherwise specified.
Edit: Additionally, as answered here, you need to change the way you're setting state to use the callback overload:
setState((oldState) => {
return { blogs: [...oldState.blogs, <AddAnother key={oldState.blogs.length} />] };
});
In most cases it makes little difference whether you set the state directly or use the callback which sets based on previous state. The latter is often used when queueing up multiple state updates in a loop, for example. But you have an edge case here.
Your AddAnother component internally references a "stale version" of addblog which always references the original state. Changing to the callback version of setState gets around this.
Other ways around this would be to restructure your code to remove the circular dependency, refactor components into their own discrete code, make use of tools like useCallback to define functions which have dependencies on state values, etc.
I've read a few articles on when to optimize rendering in React, however, I still have some doubts.
const RepairNoticeContainer = () => {
const dispatch = useDispatch();
const history = useHistory();
const { siteType, siteId } = useParams();
const data = useSelector(pageSelectors.getData);
const showRepairNotice = data.grid.cols.lg !== 36;
const handleRepairClick = () => {
dispatch(
pagesActions.copyAndRepairCurrentPage(newPageId => {
history.push(`/editor/${siteType}/${siteId}/pages/${newPageId}`);
})
);
};
return showRepairNotice ? <RepairNotice onRepairClick={handleRepairClick} /> : null;
};
As far as I can understand, it would be beneficial to use useCallbackfor handleRepairClick to avoid rerenders of <RepairNotice/>. But what about showRepairNoticevariable? Should it be wrapped in a useMemo for optimization?
const RepairNotice = ({ onRepairClick }) => {
const translate = useTranslator();
let message = translate("repair_warning");
message = message.charAt(0).toLowerCase() + message.slice(1);
return (
<MessageBox type="warning" icon="information11" className="mb-0 mt-2">
<div className="row">
<div className="col">
<b>{translate("warning")}:</b> {message}
</div>
<div className="col-auto text-right">
<Button color="danger" onClick={onRepairClick} size="small">
{translate("repair_now")}
</Button>
</div>
</div>
</MessageBox>
);
A simillar question for this example. Would it be beneficial to wrap message inside of useMemo?
const Page = ({ status }) => {
const unsavedData = status?.unsavedData ? true : false;
return (
<Fade>
<div className="Page">
<NavConfirmModal active={unsavedData} onSavePage={onSavePage} />
</div>
</Fade>
);
};
Lastly, should useMemo be used unsavedData?
Explanations would be much appreciated.
As far as I can understand, it would be beneficial to use useCallback for handleRepairClick to avoid rerenders of
That's right. while wrapping handleRepairClick you will, simply speak, prevent creating a new instance of this function so it will save RepairNotice nested component from redundant rerenders because it relies on this function in props. Another good case for useMemo is when you're rendering a list of items and each relies on the same handler function declared in their parent.
Very good useCallback explanation here.
But what about showRepairNotice variable? Should it be wrapped in a
useMemo for optimization?
It's just a simple "equation" check which is really cheap from performance side - so there is really no need in useMemo here.
Would it be beneficial to wrap message inside of useMemo?
Yes, it would. Since there are at least 3 actions javascript has to fire upon the message (charAt, toLowerCase, slice) and you don't really want this calculations to fire every time the RepairNotice component gets rerendered.
should useMemo be used unsavedData?
It might be preferable to wrap unsavedData into useMemo if NavConfirmModal will be wrapped in React.Memo or in the case of "heavy calculations". So for the current case - it would not really make a difference. (btw calculating unsavedData could be written just like !!status?.unsavedData to get boolean).
And very good useMemo explanation here.
In the React docs, it mentioned that next state shouldn't be directly computed from current state. This is because state updates are asynchronous, so you can't be assured that you are using the correct value of state.
However, in the official tutorial, you will see this function:
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares
}
]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext
});
}
You can see that the variable history is a function of state.history and state.stepNumber. Isn't this a contradiction of what was mentioned in the docs, or am I missing something?
People get a little too dogmatic about it in my opinion, but there have been enough hard to trace bugs that maybe it's justified. Ultimately, you have to know why its recommended in order to know if its ok not to in special cases.
Why is it recommended?
State updates are asynchronous and can be batched, and you may be using stale values if you update state multiple times and one or more of your updates are based on previous values. In a functional component, you have the same risks do to stale closures.
Example where state should be updated 5 times, but is only incremented once:
Functional component example:
const {useState} = React;
const Example = () => {
const [value, setValue] = useState(0);
const onClick = () => {
[1,2,3,4,5].forEach(() => {
console.log('update');
setValue(value + 1);
});
}
return (
<div>
<button onClick={onClick}>Update 5 times</button>
<div>Count: {value}</div>
</div>
)
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Class component example:
const {useState, useEffect} = React;
class Example extends React.Component {
state = {
count: 0
}
onClick = () => {
[1,2,3,4,5].forEach(() => {
this.setState({count: this.state.count + 1});
});
}
render() {
return (
<div>
<button onClick={this.onClick}>Update 5 times</button>
<div>Count: {this.state.count}</div>
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Is it ok to use normal state updates based on previous state?
It depends on your use case. If you know for an absolute certainty that you will only update state once, then technically it's safe. The problem is, while it may be safe today, tomorrow you or another developer may unknowingly change that...
At the end of the day, I think you have to ask yourself: "Do I gain any extra benefit from not using a functional update?", if so then understand the risks of future bugs and go for it (you should probably document it heavily too). But almost every time, the answer will be just use the functional update.
because this: const history = this.state.history.slice(0, this.state.stepNumber + 1); isn't actually mutating any state. it's just assigning it to a const for the local function to use. it's not actually manipulating the state itself
a few lines below it uses this.setState({}) to directly change state in the standard way
the assigning to a const is different than if you just did this: this.state.history.slice(0, this.state.stepNumber + 1) which would be directly manipulating it