cannot change usestate to change the state of the components - javascript

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.

Related

Can't understand "state" in reactjs

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

Click handler, in react, doesn't return latest change in state when it executes a 2nd time

This takes place in a functional component:
import {useEffect} from 'react';
let [clickedOnPiece, setClickedOnPiece] = useState(false);
let [testRender, setTestRender] = useState(false);
useEffect(() => {
testRenderFunction();
}, [])
function testRenderFunction() {
let el = <div onClick={onClickHandler}>Click me</div>;
setTestRender(el);
}
function onClickHandler() {
if (clickedOnPiece) {
console.log("already clicked")
return
}
console.log(clickedOnPiece); //returns false the 1st & 2nd time.
setClickedOnPiece("clicked");
}
return (
<>
{testRender}
</>
)
When I click on div for the first time, I wait until setClickedOnPiece("clicked") successfully updates clickedOnPiece to "clicked". (I check this with React Developer Tools).
When I click div the 2nd time, it doesn't log the new change in state. It still logs clickedOnPiece as false. Why is this?
Okey this problem is because useState is asyncronus. u can read more about this useState set method not reflecting change immediately.
I think the solution is add useEffect like this.
useEffect( () => {
console.log(clickOnPiece);
}
, [clickOnPiece])
If you want to toggle the state, you could do something like this:
let [clickedOnPiece, setClickedOnPiece] = useState(false);
const onClickHandler = () => {
// set the value of clickedOnPiece to the opposite of what it was
// i.e. if it was 'true', set it to 'false'.
setClickedOnPiece(!clickedOnPiece);
console.log(clickedOnPiece);
}
// call the onClickHandler on click
<div onClick={()=>onClickHandler()}>Click me</div>
Looks like you are toggling
let [clickedOnPiece, setClickedOnPiece] = useState(false);
const onClickHandler = () => {
console.log(clickedOnPiece);
setClickedOnPiece(!clickedOnPiece);
}
console.log(clickedOnPiece);
<div onClick={onClickHandler}>Click me</div>
After setting state, don't console immediately because state is an asynchronous.
onClickHandler references the old, previous variable, clickedOnPiece. I believe this is because onClickHandler is not defined in the return statement part of the functional component which would have allowed it a new onClickHandler body to be created each time. Instead, we have the old onClickHandler continually referencing the old clickedOnPiece.
This problem is known as 'stale closures' - a concept I found discussed well at the bottom of this article

React setState in If Statement

I'm making a chess game using React and I'm trying to write a function that computes the squares that should be highlighted when a certain chess piece is selected. I have the game as one component and each chess piece as a second component, and the button in the chess component calls a function from the game component when clicked.
I want to highlight the appropriate squares if and only if the selected piece is the correct color, so I had a setState call in an if statement in my handleClick() function. After reading Issue with setState() in if statement I moved the conditional so that the handleClick function is only linked to pieces of the correct color.
The issue now is that the state gets changed as desired, but for some reason the components don't rerender to reflect that change. Can someone please let me know how I could fix this? Here's my code for handling clicks:
handleClick(num){
this.setState(prevState => {
return {
gameBoard: prevState.gameBoard,
turn: prevState.turn,
selected: num
}
})
}
and here's my code for creating the board:
<div>
{
this.state.gameBoard.map((object)=>
<div className = "board-row"> {object.map((object2) =>
<Piece key={object2.at} turn = {this.state.turn} selected = {object2.selected} piece = {object2.piece} identifier = {object2.at} onClick = {() => this.handleClick(object2.at)} color = {(object2.at+Math.floor(object2.at/8))%2 === 0?"white":"black"} />)}
</div>
)
}
</div>
try:
handleClick(num){
this.setState(prevState => {
return {
...prevState,
selected: num
};
})
}
Ths issue here is mostly a question of dependencies. Your rendering is based on the gameboard field of the state, which isn't being modified. The component therefore doesn't rerender since it doesn't know that there are additional dependencies (selected) for it.
The easiest and cleanest approach I would suggest is to simply move your map function inside a useCallback (or useMemo, both should work) with the proper dependencies, then simply use it in your rendering method.
const foobar = useCallback( () => this.state.gameBoard.map((object)=>
<div className = "board-row"> {object.map((object2) =>
<Piece key={object2.at} turn = {this.state.turn} selected = {object2.selected} piece = {object2.piece} identifier = {object2.at} onClick = {() => this.handleClick(object2.at)} color = {(object2.at+Math.floor(object2.at/8))%2 === 0?"white":"black"} />)}
</div>
)
, [this.state] )
render (
<div>
{foobar()}
</div>
)

Where should functions in function components go?

I'm trying to convert this cool <canvas> animation I found here into a React reusable component. It looks like this component would require one parent component for the canvas, and many children components for the function Ball().
It would probably be better for performance reasons to make the Balls into stateless components as there will be many of them. I'm not as familiar with making stateless components and wondered where I should define the this.update() and this.draw functions defined in function Ball().
Do functions for stateless components go inside the component or outside? In other words, which of the following is better?
1:
const Ball = (props) => {
const update = () => {
...
}
const draw = () => {
...
}
return (
...
);
}
2:
function update() {
...
}
function draw() {
...
}
const Ball = (props) => {
return (
...
);
}
What are the pros and cons of each and is one of them better for specific use cases such as mine?
The first thing to note is that stateless functional components cannot have methods: You shouldn't count on calling update or draw on a rendered Ball if it is a stateless functional component.
In most cases you should declare the functions outside the component function so you declare them only once and always reuse the same reference. When you declare the function inside, every time the component is rendered the function will be defined again.
There are cases in which you will need to define a function inside the component to, for example, assign it as an event handler that behaves differently based on the properties of the component. But still you could define the function outside Ball and bind it with the properties, making the code much cleaner and making the update or draw functions reusable:
// you can use update somewhere else
const update = (propX, a, b) => { ... };
const Ball = props => (
<Something onClick={update.bind(null, props.x)} />
);
If you're using hooks, you can use useCallback to ensure the function is only redefined when any of its dependencies change (props.x in this case):
const Ball = props => {
const onClick = useCallback((a, b) => {
// do something with a, b and props.x
}, [props.x]);
return (
<Something onClick={onClick} />
);
}
This is the wrong way:
const Ball = props => {
function update(a, b) {
// props.x is visible here
}
return (
<Something onClick={update} />
);
}
When using useCallback, defining the update function in the useCallback hook itself or outside the component becomes a design decision more than anything: You should take into account if you're going to reuse update and/or if you need to access the scope of the component's closure to, for example, read/write to the state. Personally I choose to define it inside the component by default and make it reusable only if the need arises, to prevent over-engineering from the start. On top of that, reusing application logic is better done with more specific hooks, leaving components for presentational purposes. Defining the function outside the component while using hooks really depends on the grade of decoupling from React you want for your application logic.
Another common discussion about useCallback is whether to always use it for every function or not. That is, treat is as opt-in or always recommendable. I would argue to always use useCallback: I've seen many bugs caused by not wrapping a function in useCallback and not a single scenario where doing so affects the performance or logic in any way. In most cases, you want to keep a reference while the dependencies don't change, so you can use the function itself as a dependency for other effects, memos or callback. In many cases the callback will be passed as a prop to other elements, and if you memoized it with useCallback you won't change the props (thus re-render) other components independently of how cheap or costly that would be. I've seen many thousands of functions declared in components and not a single case in which using useCallback would have any down side. On the other hand most functions not memoized with useCallback would eventually be changed to do so, causing serious bugs or performance issues if the developer doesn't recognize the implications of not doing so. Technically there is a performance hit by using useCallback, as you would be creating and additional function but it is negligible compared to the re-declaration of the function that always has to happen either you use useCallback or not and the overall footprint of React and JavaScript. So, if you are really concerned about the performance impact of useCallback versus not using it, you should be questioning yourself if React is the right tool for the job.
You can place functions inside stateless functional components:
function Action() {
function handlePick(){
alert("test");
}
return (
<div>
<input type="button" onClick={handlePick} value="What you want to do ?" />
</div>
)
}
But it's not a good practice as the function handlePick() will be defined every time the component is rendered.
It would be better to define the function outside the component:
function handlePick(){
alert("test");
}
function Action() {
return (
<div>
<input type="button" onClick={handlePick} value="What you want to do ?" />
</div>
)
}
If you want to use props or state of component in function, that should be defined in component with useCallback.
function Component(props){
const onClick=useCallback(()=>{
// Do some things with props or state
},[])
return <Something {...{onClick}} />
}
On the other hand, if you don't want to use props or state in function, define that outside of component.
const computeSomethings=()=>{
// Do some things with params or side effects
}
function Component(props){
return <Something onClick={computeSomethings} />
}
For HTML tags you don't need useCallback because that will handle in react side and will not be assigned to HTML
function Component(props){
const onClick=()=>{
// Do some things with props or state
}
return <div {...{onClick}} />
}
Edit: Functions in hooks
For the use function in hooks for example useEffect, my suggestion is defining function inside useEffect, if you're worried about DRY, make your function pure call it in hook and give your params to it.
What about hooks deps? You should/could add all of your params to hooks deps, but useEffect just needs deps which should affect for them changes.
We can use the React hook useCallback as below in a functional component:
const home = (props) => {
const { small, img } = props
const [currentInd, setCurrentInd] = useState(0);
const imgArrayLength = img.length - 1;
useEffect(() => {
let id = setInterval(() => {
if (currentInd < imgArrayLength) {
setCurrentInd(currentInd => currentInd + 1)
}
else {
setCurrentInd(0)
}
}, 5000);
return () => clearInterval(id);
}, [currentInd]);
const onLeftClickHandler = useCallback(
() => {
if (currentInd === 0) {
}
else {
setCurrentInd(currentInd => currentInd - 1)
}
},
[currentInd],
);
const onRightClickHandler = useCallback(
() => {
if (currentInd < imgArrayLength) {
setCurrentInd(currentInd => currentInd + 1)
}
else {
}
},
[currentInd],
);
return (
<Wrapper img={img[currentInd]}>
<LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
<img src={Icon_dir + "chevron_left_light.png"}></img>
</LeftSliderArrow>
<RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
<img src={Icon_dir + "chevron_right_light.png"}></img>
</RightSliderArrow>
</Wrapper>);
}
export default home;
I'm getting 'img' from it's parent and that is an array.
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
const a = () => {
setCount(count + 1);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={a}>Click me</button>
</div>
);
}
export default Example;

Rendering an array of html elements

I want to render an array of html elements in my component. The reason for storing the data/html in an array is because I want to be able to dynamically load a new element depending on a button-click.
This is how I want to display my array:
<div>
{this.state.steps}
</div>
This is how I initiate my component and array:
componentDidMount() {
this.createProcessStep().then(step => {
this.setState({steps: this.state.steps.concat(step)});
});
}
export function createProcessStep() {
this.setState({processStepCounter: this.state.processStepCounter += 1});
return this.addStepToArray().then(d => {
return this.reallyCreateProcessStep()
});
}
addStepToArray = () => {
const step = {
...Some variables...
};
return new Promise(resolve => {
this.setState({
stepsData: this.state.stepsData.concat(step)
}, resolve)
});
};
"stepsData" is another array that holds data (variables) belonging to each step. "steps" on the other hand, should only hold the html.
This is how one step/element looks like:
<div>
...Some Content...
<button label="+" onClick={ () => {
this.createProcessStep().then(step => {
this.setState({
steps: this.state.steps.concat(step)
});
})
}}/>
...other content...
</div>
This button within each step is responsible for loading/adding yet another step to the array, which actually works. My component displays each step properly, however react doesn't properly render changes to the element/step, which is
to say that, whenever e.g. I change a value of an input field, react doesn't render those changes. So I can actually click on the "+"-button that will render the new html element but whenever a change to this element occurs,
react simply ignores the phenotype of said change. Keeping in mind that the changeHandlers for those steps/elements still work. I can change inputfields, radioButtons, checkboxes etc. which will do exactly what it's
supposed to, however the "re-rendering" (or whatever it is) doesn't work.
Any ideas of what I'm doing wrong here? Thanks!
While you could certainly beat your approach into working, I would advise that you take more common react approach.
You make your components to correctly display themselves from the state . ie as many steps are in the state, your component will display. Than make your add button add necessary information (information, not formated html) to the state.
Here is an example how to use component N times:
const MyRepeatedlyOccuringComponent = (n) => (<p key={n}>There goes Camel {n}</p>)
const App = () => {
const camels = [1,22,333,4444,55555]
const caravan = camels.map((n) => MyRepeatedlyOccuringComponent(n))
return(<div>{caravan}</div>
}

Categories