[React Hooks]Cannot get changed state in onClick arrow function with params - reactjs

To show my question here is a Demo code.(I'm using React Hooks and Antd.)
My Question is:
when currId state is changed and I click MyButton the state is still '' (which is the initial state). onClick event is an arrow function and in it is showModal with params, if there's no params currId can be seen changed but now with params state isn't changed. May I ask what is the reason of it and how I can get changed currId in showModal?
(operation: click 'Change CurrId' button --> setCurrId('12345') ---> click 'MyButton' ---> console.log(currId))
import React, { useState } from 'react'
import 'antd/dist/antd.css';
import { Button} from 'antd';
const MyComponent= () => {
const [currId, setCurrId] = useState('');
const changeCurrId= async () => {
setCurrSolutionId('12345');
}
const showModal = async (num:any) => {
console.log("☆ currid:");// I cannot get the currId state '12345' but ''
console.log(currId);
console.log("☆ num:");//I can get the num params 5
console.log(num);
};
return (
<>
<Button type="primary" onClick={changeCurrId}>Change CurrId</Button>
<Button type="primary" onClick={() => {showModal(5)}}>MyButton</Button>
</>
);
}

const MyComponent= () => {
const [currId, setCurrId] = useState('');
const changeCurrId= () => {
setCurrId('12345');
}
const showModal = (num:string) => {
console.log("☆ currid:");
console.log(num);
};
const changeCurrentIdAndShowModal = (id : string) => {
setCurrId(id);
showModal(id)
console.log("☆ id:");
console.log(id);
};
return (
<>
<Button type="primary" onClick={() => changeCurrId()}>MyButton</Button>
<Button type="primary" onClick={() => showModal('5')}>MyButton</Button>
<Button type="primary" onClick={() => changeCurrentIdAndShowModal('12345')}>MyButton</Button>
</>
);
}
Is this the intention you want?

Related

React: Assigning array to variable using useState to pass into modal

I made a JSON file for the upcoming NFL season. In this component I have a working fetch method that gets my data, and I've named the variable "squads". Now I want to press a button to filter out the selected team's schedule and display it in a modal. I've hard coded my button in this example. My modal component works fine, and I have {props.children} in the modal's body to accept my data.
In the code below you'll see that I'm trying to assign the filtered team to the selectedTeam variable using useState. The error message I'm getting just says my variables are undefined.
import React, { useState, useEffect } from "react";
import Modal from "./Components/Modal";
export default function App() {
const [show, setShow] = useState(false);
const [title, setTitle] = useState("");
const [squads, setSquads] = useState([]);
const [modalTitleBackground, setModalTitleBackground] = useState("");
const [image, setImage] = useState("");
const [selectedTeam, setSelectedTeam] = useState([]);
const url = "../nfl2021.json";
const fetchData = async () => {
try {
const response = await fetch(url);
const data = await response.json();
setSquads(data.teams);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
fetchData();
}, []);
// const filterTeam = (team) => {
// const theTeam = squads.filter((squad) => squad.name === team);
// setModalTitleBackground(theTeam[0].topBG);
// // setTitle(theTeam[0].name);
// setNickname(theTeam[0].nickname);
// setImage(`./images/${theTeam[0].img}`);
// setShow(true);
// };
const filterTeam = (team) => {
setSelectedTeam(squads.filter((squad) => squad.name === team));
console.log(selectedTeam);
setTitle(selectedTeam[0].name);
setModalTitleBackground(selectedTeam[0].topBG);
setImage(`./images/${selectedTeam[0].img}`);
setShow(true);
};
return (
<div className="App">
<button onClick={() => filterTeam("New England Patriots")}>
Show Modal
</button>
<button onClick={() => filterTeam("Green Bay Packers")}>
Show Modal 2
</button>
<button onClick={() => filterTeam("Cincinnati Bengals")}>
Show Modal 3
</button>
<Modal
image={image}
title={title}
backgroundColor={modalTitleBackground}
onClose={() => setShow(false)}
show={show}
>
<p>
This is the modal body using props.children in the modal component.
</p>
<p>The {title} 2021 schedule.</p>
{selectedTeam[0].schedule.map((schedule, index) => {
return (
<p>
Week {index + 1}: The {selectedTeam[0].nickname} play the{" "}
{selectedTeam[0].schedule[index].opponent}.
</p>
);
})}
</Modal>
</div>
);
}
1- In react, the state is set asynchronously. selectedTeam is not set until next render.
2- You can use find instead of filter and get rid of array access.
const [selectedTeam, setSelectedTeam] = useState({schedule: []});
...
const filterTeam = (team) => {
let temp = squads.find((squad) => squad.name === team);
setSelectedTeam(temp);
console.log(temp);
setTitle(temp.name);
setModalTitleBackground(temp.topBG);
setImage(`./images/${temp.img}`);
setShow(true);
};
...
{selectedTeam.schedule.map((match, index) => {
return (
<p>
Week {index + 1}: The {selectedTeam.nickname} play the {match.opponent}.
</p>
);
})}

Re-rendering useEffect from child component

const CompleteDriverInfo = () => {
const [DriverInfo, setDriverInfo] = useState([]);
useEffect(async () => {
await setDriverInfo(await GetFetch('driver'));
}, []);
return (
<div>
<Link to='/adddriver'>
<button className='btn btn-primary'>Add Driver</button>
</Link>
{DriverInfo.map((EachDriver) => (
<EachDriverInfo EachDriver={EachDriver} />
))}
</div>
);
};
const EachDriverInfo = ({ EachDriver }) => {
const DeleteDriver = (e) => {
POST(
'deletedriver',
{
CustomerName: EachDriver.CustomerName,
},
e
);
};
return (
<>
<h1>Name: {EachDriver.CustomerName}</h1>
<h1>Phone Number: {EachDriver.PhoneNumber}</h1>
<h1>Email: {EachDriver.Email}</h1>
<h1>Address: {EachDriver.Address}</h1>
<h1>Country: {EachDriver.Country}</h1>
<button onClick={(e) => DeleteDriver(e)} className='btn btn-primary'>
Delete Driver
</button>
</>
);
};
When I click Delete Driver in my child component, it deletes a driver from the database, but the page doesn't re-render until refresh because of useEffect's empty dependencies.
I was thinking of setting a parent state from the child component and putting that in the dependency array but it feels like an anti-pattern
It is perfectly fine to update parent state from the child component. Just pass a function to the child component, and when child component changes the state, call this function inside the child component so that the parent component can update the app state and the rerender.
Here is a sample codesandbox:
import React, { useState, useEffect } from "react";
import axios from "axios";
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPosts = async () => {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/posts`
);
setPosts(response.data);
};
getPosts();
}, []);
const handleDeletePost = (id) => {
const updatedPosts = posts.filter((p) => p.id !== id);
setPosts(updatedPosts);
};
return (
<div>
{posts.map((post) => (
<PostInfo key={post.id} post={post} deletePost={handleDeletePost} />
))}
</div>
);
};
const PostInfo = ({ post, deletePost }) => {
const DeletePost = async (id) => {
await axios.delete(`https://jsonplaceholder.typicode.com/posts/{id}`);
deletePost(id);
};
return (
<>
<h1>Id: {post.id}</h1>
<h1>Title: {post.title}</h1>
<button onClick={() => DeletePost(post.id)} className="btn btn-primary">
Delete Post
</button>
</>
);
};
export default Posts;

React State remains immutable when i try to show component onClick

I have button with onClick function like this :
<button className="btn" onClick={(e) => handleClick(user._id, e)} >Edit</button>
const [show, setShow] = useState(false)
const handleClick = (id, e) => {
e.preventDefault()
setShow(!show)
dispatch(getUser(id))}
/* And here rendering based on State */
{
show ? renderUserData() : null
}
So, problem is that setShow(!show) inside the handleClick function does not work and state of show remains false, immutable. if handleClick function contains only setShow(!show) then this function working and toggling renderUserData(), but when it contains both dispatch(getUser(id)) and setShow(!show), state of show remains false all time,please help
try to remove the e.preventDefault() you can play around with your own data but the e.preventDefault() is not useful in buttons (if you have a form try to put it in the onSubmit of the form) but the final solution must look something like this:
import React, { useState } from "react";
export default function App() {
const [show, setShow] = useState(false);
const handleClick = (id, e) => {
setShow(!show);
};
return (
<>
{show ? <h1>Data showed</h1> : null}
<button className="btn" onClick={(e) => handleClick(e)}>
Edit
</button>
</>
);
}

too many re renders with add count button

I am new to React. I have a simple page with buttons which increase the count by 1 or decrease by 1. The solution shows the right way which is by using inline functions for the minus button.However when I changed the plus button to use a regular function, I am getting too many re render.
import React, { useState } from 'react'
import randomColor from 'randomcolor'
export default function Playground() {
const [count, setCount] = useState(0)
const add = (n) =>{
setCount(n+1);
};
return (
<div>
{count}
<button onClick={() => add(count)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
<button onClick={add(count)}>+</button> immediately triggers add function when the component render.
Try this:
import React, { useState } from 'react'
import randomColor from 'randomcolor'
export default function Playground() {
const [count, setCount] = useState(0)
const add = () =>{
setCount(count+1);
};
return (
<div>
{count}
<button onClick={() => add()}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
You can use regular function or arrow function. But in your case, you were calling the add function every time it re-renders. Consider the difference in the following vanilla JavaScript code snippets:
function add() {
console.log("Add");
}
document.getElementById("Button").addEventListener("click", add);
vs
function add() {
console.log("Add");
}
document.getElementById("Button").addEventListener("click", add()); //<--
The first one is the way to go.
In the React world, the following implementations are all valid:
function ButtonWithArrowFunction() {
const handleClick = () => {
console.log("Clicked");
};
return <button onClick={handleClick}>Press Me</button>;
}
function ButtonWithInlineArrowFunction() {
return (
<button
onClick={() => {
console.log("Clicked");
}}
>
Press Me
</button>
);
}
function ButtonWithRegularFunction() {
function handleClick() {
console.log("Clicked");
}
return <button onClick={handleClick}>Press Me</button>;
}
function ButtonWithInlineRegularFunction() {
return (
<button
onClick={function handleClick() {
console.log("Clicked");
}}
>
Press Me
</button>
);
}
function ButtonWithHigherOrderFunction() {
function createHandleClickFunction() {
return function handleClick() {
console.log("Clicked");
};
}
return <button onClick={createHandleClickFunction()}>Press Me</button>;
}
you can not pass add() because it will execute the funcition and will trigger an infinite cycle. Just pass the reference add of the method, so it will execute only on the click event
import React, { useState} from 'react'
const App = () => {
const [count, setCount] = useState(0);
const add = (n) =>{
setCount(count+1);
}
return (
<div>
{count}
<button onClick={add}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
export default App;

change locale moment js

how to change date localization and displayed a new localized date?
const Locale = () => {
return (
<div>
<button onClick={() => moment.locale("en")}>English</button>
<button onClick={() => moment.locale("de")}>German</button>
<p>{moment().format("LLLL")}</p>
</div>
);
};
codesandbox example https://codesandbox.io/s/vigorous-violet-v0hvf
Here is a codesandbox with a working example https://codesandbox.io/s/peaceful-lumiere-97yto
In react if you want the component to re-render again you should make some changes to the state.
const deMoment = moment().locale("de").format("LLLL");
const enMoment = moment().locale("en").format("LLLL");
const Locale = () => {
const [momentDate, setMomentDate] = useState(deMoment);
useEffect(() => {}, [momentDate]);
return (
<div>
<button onClick={() => setMomentDate(enMoment)}>English</button>
<button onClick={() => setMomentDate(deMoment)}>German</button>
<p>{momentDate}</p>
</div>
);
};
Moment.js locale will only gets applied to new instance created after updating. So I have maintained the locale in state and I keep updating it on button click.
import React, { useState } from "react";
import moment from "moment";
const Locale = () => {
let [loc, updateLoc]= useState("en")
return (
<div>
<button onClick={() => updateLoc("en")}>English</button>
<button onClick={() => updateLoc("de")}>German</button>
<p>{moment.locale(loc) && moment().format("LLLL")}</p>
</div>
);
};
export default Locale;
You will have to notify that moment object's locale is updated.
const Locale = () => {
const [currentMoment, setCurrentMoment] = useState(moment().format("LLLL"));
const updateMomentEN = () => {
moment.locale("en");
setCurrentMoment(moment().format("LLLL"));
};
const updateMomentDE = () => {
moment.locale("de");
setCurrentMoment(moment().format("LLLL"));
};
return (
<div>
<button onClick={updateMomentEN}>English</button>
<button onClick={updateMomentDE}>German</button>
<p>{currentMoment}</p>
</div>
);
};
here after every time locale is updated, we are updating currentMoment based on new locale. Then useState does the notifying work.

Resources