How to change useState from inside of Child Component - reactjs

I am trying to import CSVs inside a Importer Component and pass on the Data to the Parent and change useState there...
So here i am trying to call said Component and pass on the useState function.
const [database, setDatabase] = useState([]);
useEffect(() => {
<Importer setdata={(data) => setDatabase([...data])} />;
}, []);
and Child Component is importing the CSV and passing on the data to be displayed after changing the State with useState:
const importAllCsv = (props) => {
text("template.csv").then((data) => {
//console.log(data);
const psv = dsvFormat(";");
//console.log(psv.parse(data));
DATABASE = psv.parse(data);
console.log(DATABASE);
props.setdata(DATABASE);
});
};
export default function Importer(props) {
return importAllCsv(props);
}

Components must start with a capital letter, also avoid returning components in useEffect when you can return them in the return part of the parent component.
As Aliyan said, try props.setdata((prevState) => [...prevState, ...DATABASE])

As per my understanding, you want to update the parent's state through a child component, for this you can simply pass the currentState (if required) and the setState function to the child as a prop using the following method :
export default function App() { //Parent Component
const [state, setState] = useState(1);
return (
<div className="App">
<div>{state}</div>
<ChildComponent setParentState={setState} currentState={state}/>
</div>
);
}
function ChildComponent({ setParentState, currentState }) {
function UpdateState() {
setParentState(currentState+1);
}
return <button onClick={() => UpdateState()}>Update State</button>;
}

Try to:
props.setdata((prevState) => [...prevState, ...DATABASE])
and try to include it on the return statement:
return (
<Importer setdata={setDatabase} />
);
not on useEffect hook.

Related

Is useCallback best way to get props data from child component to parent components?

Is useCallback best way to get props data from child component to parent components?
From my perspective there are two common options here:
Passing down a callback function (created using useCallback or otherwise):
...
function Parent() {
const [text, setText] = useState('');
// Can also just pass setText directly
function onClick() {
setText('new text');
}
return (
<Child onClick={onClick} />
);
}
function Child(props) {
return (
<button onClick={props.onClick}>Click Me</button>
);
}
Pros: Simple
Use the Context API and consume the state
If the child is deeply nested, to avoid prop drilling (and to make the state easily available to other components), you could use the ContextAPI:
TextProvider.js
...
const TextContext = createContext('');
export function TextProvider(children) {
const [text, setText] = useState('');
const value = {
text, setText
};
return <TextContext.Provider value={text}>
{children}
</TextContext.Provider>;
};
export function useText() {
const context = useContext(ClientContext);
if (context === undefined) {
throw new Error('useSocket must be used within a SocketProvider');
}
return context;
};
App.js (or whatever file renders the <Parent />) - wrap the Parent in the provider:
function App() {
return (
...
<TextProvider>
<Parent />
</TextProvider>
...
);
}
Child.js
function Child(props) {
const { text, setText } = useText();
function onClick() {
setText('hello'); // Will update "text" state on parent
}
return (
<button onClick={onClick}>Click Me</button>
);
}
Pros: Useful when passing props into a deeply nested component. This post here details some more pros/cons of the ContextAPI

React.memo issue with Redux

I have two components.
function Parent(props){
const handleClick = () => {
console.log(props.stateA);
};
return <div><Child text={stateB} handleClick={handleClick} /></div>
}
const mapStateToProps = (state) => {
return {
stateA: state.stateA // stateA will be changed somewhere else
stateB: state.stateB
}
};
export default connect(mapStateToProps)(Parent);
function Child(props) {
return <div onClick={props.handleClick}>{props.text}</div>
}
export default React.memo(Child,(prev, next) => {
return prev.text === next.text
});
My problem is when stateA is changed somewhere, clicking on Child will log the previous stateA. I can't access the latest stateA.
You can see, I don't want to Child re-render when stateA changes,it should re-render only when stateB changed. But I want to access the latest stateA in Parent when clicking on Child.
Is there any method to solve this problem?
If the Parent component is a functional component then you can use like this
const [valueA, setValueA] = useState('')
useEffect(() => {
setValueA(props.stateA)
},[props.stateA])
console.log(valueA) // latest Value of stateA
return <div><Child text={stateB} handleClick={handleClick} /></div>
I hope it'll work for you.
You should be able to access props.stateA no problem
const handleClick = () => {
console.log(props.stateA);
};
because you accessing parent's props in handleClick. So if props.stateA is stale then the logical conclusion is the parent doesn't receive the latest props. Can we see how you update props/state?
The problem you are experiencing has nothing to do with Redux.
The Parent component passes 2 props to the child: the text which is changed when needed and handleClick which is changed each render of the Parent component - a new function is created each time.
But the React.memo is checking only the text prop, so the child receives a stale handleClick quite often.
The correct solution is to wrap the handleClick with useCallback and check all props in React.memo (react does this by default).
function Parent(props){
const handleClick = useCallback(() => {
console.log(props.stateA);
}, []);
return <div><Child text={stateB} handleClick={handleClick} /></div>
}
const mapStateToProps = (state) => {
return {
stateA: state.stateA // stateA will be changed somewhere else
stateB: state.stateB
}
};
export default connect(mapStateToProps)(Parent);
function Child(props) {
return <div onClick={props.handleClick}>{props.text}</div>
}
export default React.memo(Child);
You can keep a ref to stateA so it is what is logged when you call handleClick. useRef ensures that the last value is used.
function Parent(props){
const stateARef = useRef(props.stateA);
useEffect(() => {
stateARef.current = props.stateA;
}, [props.stateA])
const handleClick = () => {
console.log(stateARef.current);
};
return <div><Child text={stateB} handleClick={handleClick} /></div>
}

React avoid all child rerendering

So I have a master component which has several children, I simplified it with the example below
const Master = ({ c1props, c2props }) => {
const [count, setCount] = useState(0)
return <div>
<div>{count}</div>
<Child {...c1props}/>
<Child {...c2props}/>
</div>
}
So here my problem is that when I update only the state "count", the components are re-rendering, which is a problem because they are pretty heavy.
I was thinking about using useMemo() inside Child as a way to avoid those uneeded re-rendering but I don't if it's the best idea.
Any idea how to address this ?
thanks
const MemoizedChild1 = React.memo(Child1, isEqual);
const MemoizedChild2 = React.memo(Child2, isEqual);
Then you use it like this:
const Master = ({ c1props, c2props }) => {
const [count, setCount] = useState(0)
return <div>
<div>{count}</div>
<MemoizedChild1 {...c1props}/>
<MemoizedChild2 {...c2props}/>
</div>
}
where isEqual is a lodash function that deeply tests the equality of props.
Generally you can use memo like this:
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);
Read more on the docs.
useMemo is a hook that memoizes a value, and memo can give you control about whenever to render your component or not.
if you want to avoid the usage of memo you may consider to split your Master, and create another Counter component which contains the count state:
const Master = ({ c1props, c2props }) => (
<div>
<Counter />
<Child {...c1props}/>
<Child {...c2props}/>
</div>
)

React rerendering children in functional component despite using memo and not having any prop changed

I have an Icon component that draw an icon and which is blinking because the parent is making it rerender for nothing. I don't understand why this is happening and how to prevent this.
Here is a snack that shows the issue.
We emulate the parent changes with a setInterval.
We emulate the icon rerendering by logging 'rerender' in the console.
Here is the code:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
// or any pure javascript modules available in npm
let interval = null
const Child = ({name}) => {
//Why would this child still rerender, and how to prevent it?
console.log('rerender')
return <Text>{name}</Text>
}
const ChildContainer = ({name}) => {
const Memo = React.memo(Child, () => true)
return <Memo name={name}/>
}
export default function App() {
const [state, setState] = React.useState(0)
const name = 'constant'
// Change the state every second
React.useEffect(() => {
interval = setInterval(() => setState(s => s+1), 1000)
return () => clearInterval(interval)
}, [])
return (
<View>
<ChildContainer name={name} />
</View>
);
}
If you could explain me why this is happening and what is the proper way to fix it, that would be awesome!
If you move const Memo = React.memo(Child, () => true) outside the ChildContainer your code will work as expected.
While ChildContainer is not a memoized component, it will be re-rendered and create a memoized Child component on every parent re-render.
By moving the memoization outside of the ChildContainer, you safely memoize your component Child once, and no matter how many times ChildContainer will be called, Child will only run one time.
Here is a working demo. I also added a log on the App to track every re-render, and one log to the ChildComponent so you can see that this function is called on every re-render without actually touching Child anymore.
You could also wrap Child with React.memo directly:
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
// or any pure javascript modules available in npm
let interval = null;
const Child = React.memo(({ name }) => {
//Why would this child still rerender, and how to prevent it?
console.log("memoized component rerender");
return <Text>{name}</Text>;
}, () => true);
const ChildContainer = ({ name }) => {
console.log("ChildContainer component rerender");
return <Child name={name} />;
};
export default function App() {
const [state, setState] = React.useState(0);
const name = "constant";
// Change the state every second
React.useEffect(() => {
interval = setInterval(() => setState(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
console.log("App rerender");
return (
<View>
<ChildContainer name={name} />
</View>
);
}

Trigger Child component method when prop changes

I'm trying to make a child functional component to update when a Parent component changes a value in its state that I'm passing to a this child component as a prop.
The child component "receives" the value correctly and displays the prop value, but the method does not run again.
Child component
import React from 'react'
const MyCustomTable = props => {
const {
data = [],
} = props
const finalData = getSalesData() //This is the method i want to run when the selectedMonth prop updates
const getSalesData = () => {
//It does some calculations with the prop called data
}
return (
<Box>
{JSON.stringify(props.selectedMonth.value)}
<Table
data={finalData}
/>
</Box>
)
}
SalesByFamilyBU.propTypes = {}
export default MyCustomTable
The JSON.stringify line displays the changes correctly but I guess the getSalesData() is not automatically executed.
While you could use some lifecycle method or the useEffect hook to achieve what you want to do, I would rather use a functional approach.
In your example, finalData is a derived value of props.data and props.selectedMonth. You could then compute finalData directly from these props:
const MyCustomTable = props => {
const {
data = [],
} = props;
const filterData = (data, selectedMonth) => data.map(dataPoint => ({
...dataPoint,
selected: dataPoint.month === selectedMonth,
}); // or whatever, use your function logic here
const finalData = filterData(data, props.selectedMonth.value);
return (...);
};
If you really needed to call a function each time data is changing (ex. to fetch data elsewhere) you could use something like that:
const MyComponent = ({ data }) => {
const [finalData, setFinalData] = useState([]);
const myFunction = () => {
const newData = ... // whatever you need to do
setFinalData(newData);
};
useEffect(myFunction, [data]);
return ...;
};

Resources