How to reRender the Page after Splice Method in reactJS? - javascript

After onClick method to splice array, data seems to delete but page isn't updating. How to reRender or update the page to reflect the changes?
Home.js:
import React from "react";
import "./HomeStyles.css";
import HomeData from "./HomeData";
function Home() {
function handleDelete(id) {
var index = HomeData.map(function (e) {
return e.id;
}).indexOf(id);
HomeData.splice(index, 1);
}
return (
<>
<section className="home_section">
<div className="home_container">
{HomeData.map((item) => {
return (
<div className="Heading_container" key={item.id}>
<h1 className="home_heading">{item.heading} </h1>
<button onClick={handleDelete}>Delete</button>
</div>
);
})}
<button className="submit_btn">Submit</button>
</div>
</section>
</>
);
}
export default Home;
Data:
const HomeData = [
{
id: 1,
heading: 'This is first Heading'
},
{
id: 2,
heading: 'This is Second Heading'
},
]
export default HomeData;
I have tried using useNavigate from react-router-dom and history, but it didn't work.

In React functional components you can use a hook called useState. With this hook you can get and set the data however you want it.
const [data, setData] = useState(homeData);
Mutating state however is a big no-no in the React ecosystem because of the fact that it heavily practices the concept of immutability. Splice mutates the state by deleting or adding to the element itself.
Instead of mapping and splicing you can use filter with the setter. Filter is immutable, because it creates a shallow copy. You want to create a shallow copy, but without the item that has the id given as a parameter in your function. This would translate to the following code:
setData(homeData.filter(home => home.id !== id));
Now all you have to do is map through the state "data" instead of the homeData directly.

Maybe you can utilize state for this, can use useState hooks
It will be something like this:
import React, {useState} from "react";
import "./HomeStyles.css";
import HomeData from "./HomeData";
function Home() {
const [data,setData] = useState(HomeData)
function handleDelete(id) {
const newData = data.filter((e) => e.id !== id)
setData(newData)
}
return (
<>
<section className="home_section">
<div className="home_container">
[don't forget to use the state here] >>> {data.map((item) => {
return (
<div className="Heading_container" key={item.id}>
<h1 className="home_heading">{item.heading} </h1>
<button onClick={handleDelete}>Delete</button>
</div>
);
})}
<button className="submit_btn">Submit</button>
</div>
</section>
</>
);
}
export default Home;

Issue
In the current implementation the code is mutating an object that ins't part of any React state, so React isn't aware that anything needs to be rerendered.
Things to keep in mind:
Array.prototype.splice does an in-place mutation of the array it operates over.
The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place. To access part of an array without modifying it, see slice().
React components rerender for one of three reasons:
A local component state update is enqueued, component and sub-ReactTree rerender
A passed props value is updated, component and sub-ReactTree rerender
The parent component rerendered (because state and/or props updated)
Solution
To correctly render and update the HomeData array it necessarily should be part of a React component state. When updating React state, all state, and sub-state, necessarily needs to be a new object reference. This is because React uses a shallow reference equality check. It's far more common to use Array.prototype.filter to filter an existing array and return a new array reference.
Home Example:
import React from "react";
import "./HomeStyles.css";
import HomeData from "./HomeData";
function Home() {
const [homeData, setHomeData] = React.useState(HomeData); // <-- initialize state
const handleDelete = (id) => {
setHomeData(data => data.filter(el => el.id !== id)); // <-- filter and return new array
};
return (
<section className="home_section">
<div className="home_container">
{homeData.map((item) => ( // <-- map homeData state
<div className="Heading_container" key={item.id}>
<h1 className="home_heading">{item.heading}</h1>
<button
button="button" // <-- should be explicit with button type
onClick={handleDelete}
>
Delete
</button>
</div>
))}
<button
className="submit_btn"
type="submit" // <-- should be explicit with button type
>
Submit
</button>
</div>
</section>
);
}
export default Home;

You should use the useState hooks to update the view
import React, { useState } from "react"; //imported useState
import "./HomeStyles.css";
import HomeData from "./HomeData";
function Home() {
const [homeData, setHomeData] = useState(HomeData); //Added here
function handleDelete(id) {
const newData = homeData.filter((e) => e.id !== id)
setHomeData(newData)
}
return (
<>
<section className="home_section">
<div className="home_container">
{homeData.map((item) => { //changed state here
return (
<div className="Heading_container" key={item.id}>
<h1 className="home_heading">{item.heading} </h1>
<button onClick={handleDelete}>Delete</button>
</div>
);
})}
<button className="submit_btn">Submit</button>
</div>
</section>
</>
);
}
export default Home;

Related

how do i pass a the input value of the textfield from some component to another component in reactjs?

I am trying to pass the value of the text area from some component in reactjs to be used in another react component. the component value is stored in the first component in a useState hook so I want to access it in another component and run map() function around it . Is this possible in reactjs ? I don't want to put the whole thing in app.js because that is just plain HTML which I don't want. I want to use reactjs function components instead ?
first component:
import React, { useState, useRef, useEffect } from "react";
function Firstcomp() {
const [quotes, setQuotes] = useState(["hi there", "greetings"]);
const reference = useRef();
function sub(event) {
event.preventDefault();
setQuotes((old) => [reference.current.value, ...old]);
console.log(quotes);
return;
}
return (
<>
<div>
<div>
<div>
<h4>jon snow</h4>
</div>
<form onSubmit={sub}>
<textarea
type="textarea"
ref={reference}
placeholder="Type your tweet..."
/>
<button type="submit">Tweet</button>
</form>
{quotes.map((item) => (
<li key={item}>{item}</li>
))}
{/* we can use card display taking item as prop where it
will do the job of filling the <p> in card entry */}
</div>
</div>
</>
);
}
export default Firstcomp;
second component
import React from "react";
function SecondComp(props) {
return (
<div>
<p>{props.message}</p>
</div>
);
}
export default Secondcomp;
Use a global management state like Recoil, Redux ot Context
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
<UserContext.Provider value="Reed">
<User />
</UserContext.Provider>
)
}
function User() {
const value = React.useContext(UserContext);
return <h1>{value}</h1>;
}
on the exemple above we used useContext hook to provide a global variable "value", even its not declared directly in User component, but you can use it by calling the useContext hook.
in this exemple the return value in the user component is "Reed"

why role base parent of two child component have different state if update in one of these child component in reactjs

I want to know I have one parent component and two child components and these child components are separated according to the user role. I have passed the parent state in these child components. In the beginning, both child components have the same state value, but if I update the state value in one child component, it will not update the state value in another component why.
Here is an example code.
import React, { useEffect, useState } from "react";
import Demo1 from "./Demo1";
import Demo2 from "./Demo2";
const Demo = () => {
const [staVal, setStaVal] = useState("hi");
console.log(staVal);
const user = JSON.parse(localStorage.getItem("auth"));
return (
<div>
{user.role === "user" ? (
<Demo1 staVal={staVal} handler={() => setStaVal("google")} />
) : (
<Demo2 staVal={staVal} />
)}
</div>
);
};
export default Demo;
Demo1 component:
import React from "react";
const Demo1 = ({ staVal, setStaVal, handler }) => {
return (
<>
<div>demo1:{staVal}</div>
<button onClick={handler}>clik</button>
</>
);
};
export default Demo1;
Demo 2 component:
import React from "react";
const Demo2 = ({ staVal }) => {
return <div>demo2:{staVal}</div>;
};
export default Demo2;
Accessing localStorage is a side effect.
Side effects cannot be called from the render method (for Class components) or the top level (function components).
In your code, access the localStorage inside useEffect(()=>{}, []) or
inside componentDidMount if you want to make it a class component.
use the useEffect to get the item from the local storage.
const [user,setUser]=useState(null);
useEffect(()=>{
const currentUser = JSON.parse(localStorage.getItem("auth"));
setUser(currentUser)
},[])
return (
<div>
{user.role === "user" ? (
<Demo1 staVal={staVal} handler={() => setStaVal("google")} />
) : (
<Demo2 staVal={staVal} />
)}
</div>
);
};

Why onClick event doesn't change the props.title in React

I wanna change the title by clicking the button but it doesn't change, can I have an explanation why is that happens?
import './ExpenseItem.css';
import ExpenseDate from './ExpenseDate';
import Card from './Card';
function ExpenseItem(props){
let title = props.expenseTitle;
function clickedFunc(){
title = "Update!";
}
return(
<Card className='expense-item'>
<ExpenseDate expenseDate={props.expenseDate}></ExpenseDate>
<div className='expense-item__description'>
<h2>{title}</h2>
<div className='expense-item__price'>
₹{props.expenseAmount}
</div>
</div>
<button onClick={clickedFunc}>Click</button>
</Card>
);
}
export default ExpenseItem;
This is not how data is handled with React.
The title should be stored in a state variable (see useState).
Once the data is stored in a state variable, you will have to set it with setState. When setState is called in React, the component holding the state variable re-renders. This will in turn cause your ExpenseItem component to re-render because it is a child component of whatever higher level component passed it props.
In your parent component, you should see something like:
require { useState } from 'react';
const ParentComponent = (props) => {
const [title, setTitle] = useState('Original Title');
...
...
...
return (
<div className="ParentComponent">
<ExpenseItem
title={title}
setTitle={setTitle}
expenseAmount={expenseAmount}
/>
</div>
)
}
Then, in your clickedFunc() function:
function clickedFunc() {
props.setTitle("Update!");
}

React hooks setter doesn't trigger rerender after array splice

I have a parent component like this:
import React from "react";
import Test from "./Test";
function App() {
const [configs, setConfigs] = React.useState([1, 2, 3])
return (
<div>
{configs.map(((oneConfig, index) =>
<Test
config={oneConfig}
remove={() => {
configs.splice(index, 1);
setConfigs(configs);
console.log(222)
}}
/>))
}
</div>
);
}
export default App;
and the Test component is like this
export default function Test(props) {
return(
<div style={{border: "solid 1px", margin: "5px"}}>
<p>This is config {props.config}</p>
<a href="/" onClick={() => props.remove()}>delete config{props.config}</a>
</div>
)
}
I wish that when I click "delete config [i]" a tag, the corresponding div will be removed, but it isn't happening that way.
After setting a breakpoint in the parent component at the beginning of the function and console line, I found out that after executing setConfigs method, the parent component isn't re-rendering.
Any idea why this is so and how can I achieve my goal of removing an element from configs?
The problem's in the remove prop:
configs.splice(index, 1);
splice mutates the configs state, and mutating state directly in React is a no-no.
Try rewriting your remove prop like so:
remove={() => setConfigs(prev => {
let left = prev.slice(0, index); // Everything before configs[index]
let right = prev.slice(index + 1); // Everything after configs[index]
return [...left, ...right];
})}
This will set the configs state equal to the previous state without the oneConfig whose delete button you clicked.

How to pass a React Hook to a child component

I want to pass the setter of a React Hook to a Child Component. So that a button in the child component updates the state via setter which is saved in the Parent Component. I tried following setup but I get an error message :
TypeError: setshowOptionPC is not a function
onClick
Is my approach even possible? And if not how could I possibly do that structure using a React Hook.
Below a simplified version of my code:
import React, { useState } from "react";
function ChildComponent({ setshowChildOptionBC, setshowChildOptionPC }) (
<div>
<button
onClick={() => {
setshowChildOptionPC(false);
setshowChildOptionBC(true);
}}
>
BC
</button>
<button
onClick={() => {
setshowChildOptionPC(true);
setshowChildOptionBC(false);
}}
>
PC
</button>
</div>
);
function ParentComponent() {
const [showOptionBC, setshowOptionBC] = useState(true);
const [showOptionPC, setshowOptionPC] = useState(false);
return (
<div>
<ChildComponent
setshowChildOptionBC={setshowOptionBC}
setshowChildOptionPC={setshowOptionPC}
/>
{showOptionBC && <div>BC</div>}
{showOptionPC && <div>PC</div>}
</div>
);
}
export default ParentComponent;
I think you just forgot to destructure props in your child component.
This might help.
function ChildComponent({setshowOptionBC, setshowOptionPC}) {..

Categories