next.js how to change parent state from child component without triggering a button? - reactjs

I have a SlaTable component to calculate and show a table of time values.
Below this table, I want to show a diagram based on the calculated values of SlaTable.
How can I return values from SlaTable to use them in another component?
<SlaTable
times={times}
monthList={monthList}
balanceValue={balanceValue}
slaFilter={slaFilter}
billableFilter={billableFilter}
/>
<Diagram
labels={chartLabels}
values={chartValues}
/>
Thanks in advance
Frank

I think I found the answer.
I have to use useEffect inside my component. So my solution looks like this:
import { useState } from "react";
function Sla(props) {
const [chartData, setChartData] = useState([]);
const changeChartData = (arg) => {
setChartData(arg);
};
return (
<>
<SlaTable
times={times}
monthList={monthList}
balanceValue={balanceValue}
slaFilter={slaFilter}
billableFilter={billableFilter}
changeChartData={changeChartData}
/>
</>
)
}
And inside the SlaTable component it's like this:
import { useEffect } from "react";
function SlaTable(props) {
const changeChartData = props.changeChartData;
const monthList = props.monthList;
let totalMonth = [];
// ----------------------------------------
// fill total month with zeros
// ----------------------------------------
monthList.forEach(() => {
totalMonth.push(0);
});
// call the changeChartData function when component
// finished rendering
useEffect(() => {
changeChartData(totalMonth);
}, [totalMonth.length]);
return (
<>
HTML output
</>
);
}
If I would not use useEffect inside the component, I would get an error that I cannot update a component while rendering a different component

Related

How to keep on adding the component on react state change.?

I have a simple requirement where there is a successBar component
<successBar msg={"message"}/>
this one I want to insert to the dom on some state change. lets say we have a state,
successMessage, if we set a message in this state, then we need to how the success bar
like:-
{
this.state.successMessage && <SuccessBar msg={this.state.successMessage} />
}
the above code works,
here the main requirement is when the successMessage is updated it should add a new <Successbar /> and not replace the previous, bar.
and I don't want to track the previous success msg.
the <SuccessBar /> component will dismiss itself after a few seconds.
how to achieve this, in react.
You can have an Array with the SuccessBar components you want to render, and push to that array on every successMessage change.
Like this:
import { useState, useEffect } from "react";
function SuccessBar({ msg }) {
return <div>{msg}</div>;
}
export default function App() {
const [successMessage, setSuccessMessage] = useState("");
const [successComponents, setSuccessComponents] = useState([]);
useEffect(() => {
if (successMessage) {
setSuccessComponents((prevArray) => [
...prevArray,
<SuccessBar msg={successMessage} />
]);
}
}, [successMessage]);
return (
successComponents.map((Component) => Component)
);
}

Accessing state or props from functional component

i'm trying to accessing variable/state in functional component from other component. i found out i can save the in some global state like redux or local storage. but i don't want to use any of them. i also found out about lifting state up but can't understand about it.
below is an example for my case. let's say i want to generate a lucky number from functional component GenerateLuckyNumber. and i call the component from showLuckyNumber. but i need to save all the list of lucky number that have been generated. i create a hook state to store all lucky number. but i dont know how to retrieve them from GenereateLuckyNumber
any idea how to to it?
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { useState, useEffect } from 'react';
export const GenerateLuckyNumber = () => {
const [luckNo, setluckNo] = useState(1)
const changeCatName = () => {
setluckNo(Math.floor(Math.random() * 100))
}
return (
<>
<TouchableOpacity onPress={() => changeCatName()}>
<Text>Hello, Your Lucky Number is {luckNo}!</Text>
</TouchableOpacity>
</>
);
}
const showLuckyNumber = () => {
const [listLuckyNumber, setlistLuckyNumber] = useState()
//how to save all generated number from functional component to state in this component
return (
<>
<GenerateLuckyNumber/>
<GenerateLuckyNumber/>
<GenerateLuckyNumber/>
</>
);
}
export default showLuckyNumber;
Your component can provide an optional callback function as a prop, which it would invoke whenever it "generates a lucky number". For example:
export const GenerateLuckyNumber = ({onGenerate}) => {
And within the component:
const changeCatName = () => {
const luckyNumber = Math.floor(Math.random() * 100);
setluckNo(luckyNumber);
onGenerate && onGenerate(luckyNumber); // invoke the callback
}
Then any component which renders a <GenerateLuckyNumber/> can optionally supply it with a callback:
const showLuckyNumber = () => {
const [listLuckyNumber, setlistLuckyNumber] = useState()
const handleNewLuckyNumber = (num) => {
// do whatever you like with the newly generated number here
};
return (
<>
<GenerateLuckyNumber onGenerate={handleNewLuckyNumber}/>
<GenerateLuckyNumber onGenerate={handleNewLuckyNumber}/>
<GenerateLuckyNumber onGenerate={handleNewLuckyNumber}/>
</>
);
}

How to avoid this message warning "Maximum update depth exceeded..." on NextJs

on NextJs i not understand, how useEffect work. What i need to do, to stop of receiving this warning message
"Maximum update depth exceeded":
The Code bellow is the page, that call a component ListContainer, this page add a item to container.
The page JSX:
import { useState } from "react";
import AppLayout from "../components/AppLayout";
import ListContainer from "../components/ListContainer";
export default function componentCreator(){
const [item,setItem] = useState([])
/* add item to container */
function addItem(){
let newItem = item
newItem.push({
produto: 'Skol 350ml',
preco: '1200,00',
quantidade: 'cx c/ 15 unidades'
})
setItem(newItem)
}
return (
<AppLayout>
<ListContainer items={item} setItems={setItem}/>
<div className="productcardbuttonshow" onClick={() => addItem()}>ADICIONAR</div>
</AppLayout>
)
}
Bellow the component that handle the items, remove or add. But it works, but on console trigger warning messages about update.
Component ListContainer.jsx:
import { useState,useEffect } from "react";
export default function ListContainer(props){
const [html,setHTML] = useState(null)
const [item,setItem] = useState(props.items)
/* refresh html container */
useEffect(() => {
const itemHTML = item.map((itemmap,id) => {
return (
<div id={id} onClick={() => delItem(id)} className="itemProposta">
{itemmap.produto} - {itemmap.quantidade} - R$ {itemmap.preco}
</div>
)
})
setHTML(itemHTML)
})
/* remove item from container */
function delItem(id){
let itemlist = props.items
itemlist.splice(id,1)
props.setItems(itemlist)
}
return (
<>
{html}
</>
)
}
You are getting into an infinite loops of renders. This code is responsible:
useEffect(() => {
const itemHTML = item.map((itemmap,id) => {
return (
<div id={id} onClick={() => delItem(id)} className="itemProposta">
{itemmap.produto} - {itemmap.quantidade} - R$ {itemmap.preco}
</div>
)
})
setHTML(itemHTML)
})
This callback inside useEffect will run after every render, because there is no dependency array. That means after every render, setHTML(itemHTML) is called. And even if the constituent objects of the array itemHTML are same, a new reference of the array is created. A new reference is created because .map() returns a new reference of the array. And although render and update works correctly, infinite rendering is happening.
Consider adding a dependency array to useEffect. For example:
useEffect(() => {
/* function body */
},[props.items]);
Now useEffect callback only runs if props.items reference changes.
Side note (unrelated to your question):
In the below code,
function addItem(){
let newItem = item
newItem.push({
produto: 'Skol 350ml',
preco: '1200,00',
quantidade: 'cx c/ 15 unidades'
})
setItem(newItem)
}
You should do let newItem = [...item], otherwise you are not creating a new reference of item array and setItem(newItem) is basically useless in that case.

How to to trigger useEffects on windows.variable Global Variable change

I am loading the two files and i have a variable called routes that i want to have changed base on a click from another component. I have a global variable called window.showhide thats boolen and another one window.route which is an object array. i want to toggle window.showhide from another component and have useEffect trigger. using window.showhide useEffects does not trigger. i just want to change window.route to route2 from another component. please help me figure this out.
import route1 from "./routes1"
import route2 from ".routes2"
window.showhide = false
window.route = route1
const routes = window.route
export default function App() {
useEffect(() => {
if(window.showhide){
routes = route2
} else {
routes = route1
}
}, [window.showhide]);
}
useEffect does not have the ability to listen for variables to change, and then cause a rerender of your component. Rather, your component has to already be rerendering due to something else, and then the dependency array decides whether or not to run the extra code found in the useEffect.
If you want to render a component in react, you need to set state. So the typical solution for your kind of problem is to have a state variable in App, and then pass down a function that lets a child set that state.
export default function App() {
const [routes, setRoutes] = useState(route1);
const showHide = useCallback((value) => {
setRoutes(value ? route1 : route2);
}, []);
return (
<ChildComponent showHide={showHide} />
);
}
// In the child:
function ChildComponent({ showHide }) {
return <div onClick={() => showHide(true)} />
}
If the components are very far removed from eachother, then passing down via props may be a pain, in which case you can use context instead:
const ShowHideContext = createContext(() => {});
export default function App() {
const [routes, setRoutes] = useState(route1);
const showHide = useCallback((value) => {
setRoutes(value ? route1 : route2);
}, []);
return (
<ShowHideContext.Provider value={showHide}>
<ChildComponent />
</ShowHideContext.Provider>
);
}
// Elsewhere:
function Example () {
const showHide = useContext(ShowHideContext);
return <button onClick={() => showHide(true)} />
}

How can I change the state of one React component based on the value of its sibling?

I'm attempting to build a form in React that has some auto-filling features. I'm trying to create the ability to 'lock' the value of LossOver50k to either 'Yes' or 'No' based on the ReplacementCost value to prevent input errors. The code I have right now is
import React, { useState } from "react";
import ReplacementCost from "./ReplacementCost";
import LossOver50K from "./LossOver50K";
const LossAmount = () => {
const [replacementCost, setReplacementCost] = useState("");
const [highValue, setHighValue] = useState("No");
const handleChange = (newReplacementCost) => {
setReplacementCost(newReplacementCost);
if (replacementCost >= 50000) {
setHighValue("Yes");
}
};
// const changeHighValue = (newHighValue) => {
// setHighValue(newHighValue);
// };
return (
<div>
<ReplacementCost value={replacementCost} onChange={handleChange} />
<LossOver50K value={highValue} />
</div>
);
};
export default LossAmount;
My commented piece was working as an onChange handler on the LossOver50k component but I'm trying to manipulate the value of LossOver50k if the value of Replacement Cost is equal to or greater than 50,000. I have each of these components successfully updating their state when I input the values directly by passing the props down to the child components. Any ideas?
TIA!
Your condition should be with the most updated value and not with a staled one (as setState is async):
const handleChange = (newReplacementCost) => {
setReplacementCost(newReplacementCost);
if (newReplacementCost >= 50000) {
setHighValue("Yes");
}
};

Resources