How to pass props from functional component to class component - reactjs

I'm quite new to functional components and I've been on this for hours.
I simply want to pass a prop from a parent functional component to a class child component so that I will use the updated prop (which will be a boolean) to change the display of an element. That's all.
In this scenario, I want to switch the sidenav from opened to closed based on the prop.
Here is the parent component (functional):
let opened = false;
function openSidenav(){
opened = !opened;
}
const [sidebarOpened, setOpened ] = useState(opened);
return (
<Sidebar sidebarOpened={opened} />
)
and the child component (class):
componentDidUpdate(prevProps) {
if(this.props.sidebarOpened !== prevProps){
this.setState({ sidebar: true});
}
}
It's just not working, the child component isn't receiving changes, I guess I didn't pass the prop rightly. I know the code just needs correction but I don't know where I'm not getting it.
Thank you.

The argument to useState is only used once. You need to set the state in openSidenav function to trigger a re-render.
Parent
function openSidenav(){
setOpened(prev => !prev);
}
const [sidebarOpened, setOpened ] = useState(false);
return (
<Sidebar sidebarOpened={sidebarOpened} />
)
Also in child component, use prevProps.sidebarOpened (not just prevProps).
Child
componentDidUpdate(prevProps) {
if(this.props.sidebarOpened !== prevProps.sidebarOpened){ //<---- see here
this.setState({ sidebar: this.props.sidebarOpened});//<---- see here
}
}
p.s - Also, since you are using props directly in child component, you can consider to not to copy props into state.

In parent,
const [sidebarOpened, setOpened ] = useState(false);
function openSidenav(){
setOpened(!sidebarOpened);
}
return (
<Sidebar sidebarOpened={sidebarOpened} />
)
And in child component class directly use this.props.sidebarOpened instead of copying over the prop to state. If you intend to edit the value of sidebarOpened in the child component, pass setOpened to the child component as a prop and use that to edit the value.

Related

How to pass data from parent component to children but from children prop ( like Formik)

I will try to explain what I want. Trying to make something like Formik.
I want something like this:
<Parent>
{(someData)=>(
<>
{console.log(someData)}
</>
)}
</Parent>
Parent component have children prop. How to pass this someData from Parent component?
Parent component is:
const Parent =({children})=>{
const value='Pass to children';
return ({children})
}
How to pass this value through children porp?
Like on this link:https://formik.org/docs/guides/react-native
I'm not exactly sure what you are trying to achieve but I do think I can help!
In react we can either pass props down to children components or we can use the useCallback hook function to pass data back to the parent, I'll show you what I mean.
As you can see in this parent element we can declare our properties which we would like to pass to our child. In this case we are passing a string with the variable name props. and a function with the variable name callLogger.
const Parent = () => {
const loggerFunction = (argument) => {
console.log(argument)
})
return (
<Child props={'hello world'} callLogger={loggerFunction}/>
)
}
We can then use these values inside of our child component like so. In this example props holds a value of 'hello world'. When we use the useCallback hook, we pass in an argument, 'props'. Which is then passed back to parent component where it is called by the function we have passed in.
const Child = ({props, callLogger}) => {
const logger = useCallback((argument) => {
callLogger(argument);
})
return (
<div onClick={e => {e.preventDefault(); logger(props)}/>
)
}
It may also be worth noting that in order to use this child component anywhere else you will still have to pass it the required properties in order for it to work. Hence why it is is safest to plan which components will require this data and find the best suited parents component to handle all of the logic of the two containers.
I think you are talking about render props
const Parent = ({children}) =>{
const value='Pass to children';
return children(value)
}
you have to call children as function.
and you use it like this:
<Parent>
{(someData)=>(
<>
{someData}
</>
)}
</Parent>
or like this:
<Parent children={(someData) => <>{someData}</>} />

How to pass data from child component to parent component without the child component rerendering in React?

My child component is an accordion with controls in it. The problem is, everytime you input data on the controls and it triggers an onChange event which passes its data to its parent component, the accordion closes/collapses and I don't need that behavior.
The accordion is just imported from a library and I can't change its behavior.
Is there anyway to prevent this or pass props/data from child component to parent component without rerendering/restarting DOM of child component?
Parent component:
const ParentComponent = () => {
const handleChangeChildAccordion = (e) => {
console.log(e);
}
return (
<ChildComponentAccordion currentData={currentData} onChangeTPAccordion={handleChangeChildAccordion}/> }
);
}
Child Component:
const ChildComponent = (props) => {
const onDataChangeHandler = (e) => {
props.onChangeTPAccordion(e);
};
return (
<Accordion onChange={onDataChangeHandler}/>
);
}
The child component doesn't remount, it rerenders. Normally, a component rerenders any time any its parent rerenders. To avoid that you:
Avoid changing the child component's props, and
Memoize the child component so it doesn't rerender when its props don't change in a way it cares about.
You do the first by ensuring that, for instance, callbacks you pass the component are stable (they don't change on every render). In a function component, you do it by using useMemo, useCallback, and/or useRef. In a class component, you typically do that by making the callbacks properties on the component instance that aren't recreated.
You do the second by using React.memo (if it's a function component) or for a class component by extending PureComponent (for simple memoization) or implementing shouldComponentUpdate.
You haven't given us much code to work with, so here's a simple example:
const { useState, useCallback, useRef } = React;
const log = (...msgs) => console.log(...msgs);
const Child1 = React.memo(({value, doUpdate}) => {
log(`Child1 rendering`);
return <div>
Child1 - Value: {value} <input type="button" value="+" onClick={doUpdate} />
</div>;
});
const Child2 = React.memo(({value, doUpdate}) => {
log(`Child2 rendering`);
return <div>
Child2 - Value: {value} <input type="button" value="+" onClick={doUpdate} />
</div>;
});
const Parent = () => {
log(`Parent rendering`);
const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);
// `useCallback` remembers the first function you pass it and returns
// it to you, only returning the new one you pass it if the
// dependencies array changes. This is one way to create a stable
// callback.
const update1 = useCallback(() => setCounter1(c => c + 1), []);
// `useRef` gives you an object you can put arbitrary properties on,
// which is another way to have a stable callback.
const update2Ref = useRef(null);
if (!update2Ref.current) {
update2Ref.current = () => setCounter2(c => c + 1);
}
const update2 = update2Ref.current;
return <div>
<Child1 value={counter1} doUpdate={update1} />
<Child2 value={counter2} doUpdate={update2} />
</div>;
};
ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
TypeScript playground with types added
That shows two different ways of making acallback stable (useCallback and useRef). (useMemo is what useCallback uses under the hood, so I didn't do an example of it.)
In that example, notice how the child telling the parent to update its counter doesn't cause the other child to re-render. That's because:
We don't change the other child's props (because our callbacks are stable and the other child's counter didn't change), and
We've memoized the child components
You can customize whether the component re-renders by passing a second argument to React.memo which is a function that says whether the new props coming in are "the same" (for rendering purposes) as the previous ones. By default, React.memo does a simple === comparison on each prop. And again, for class components, PureComponent does the simple === comparison, or you can implement shouldComponentUpdate yourself for more finely-tuned checks.

How to make functional component rerender properly on parents state chage?

please help me figure out why the component works like that.
I have a functional component something like this:
function MyComponent(props) {
const { dataArr } = props;
[ownDataArr, setOwnDataArr] = useState([...dataArr]);
// doesn't change after changing in the state of the parent component
console.log(ownDataArr);
return (
// ownDataArr is used here
);
It receives dataArr from parent component via props (parent component contains this in state). And when changed in parent component after MyComponent rerenders, ownDataArr stays the same. What am I doing wrong?
P.S. The child component needs the state, since it must be able to change the received data without constantly sending it to the parent.
You can do this to update the state on props change
useEffect(() => {
setOwnDataArr(dataArr)
}, [dataArr])
This is because state initialize on the first render of component with the props and when the props change, we have to update the state using useEffect
function MyComponent(props) {
const { dataArr } = props;
[ownDataArr, setOwnDataArr] = useState([...dataArr]);
// add useEffect wich depend on dataArr changing,
// because initial state is memoized by useState
useEffect(() => setOwnDataArr(dataArr), [dataArr])
return (
// ownDataArr is used here
);
}

Unmounting a component when triggered through parent component

I have a parent component and child component. Child component will be rendered through parent based on a condition. So, when child gets rendered, all useEffect and other code gets executed and I'm able to achieve componentDidMount kind of methods. I'm not directly accessing child component but only use Parent to render it based on parent's props.
Once my child component is visible, I call a method which resets 'doesChildExists' prop and re-render ParentComponent and since the condition is failed, ChildComponent is not rendered (unmounted). Once, ChildComponent is about to unmount, I need to do some stuff, but existing useEffect doesn't seem to get called. How do I handle this? Please help!!
Please, let me know if this is not the right approach, and I have handle rendering of child in a different way.
const ChildComponent: FunctionComponent<Props> = (props: Props) => {
useEffect(() => {
return () => {
// handle unmount
}
}, []);
return (
<div class='textDiv'>
This is content
</div>
)
}
const ParentComponent: FunctionComponent<ParentProps> = (props: ParentProps) => {
return (
<>
{doesChildExists &&
createPortal(<ChildComponent />, document.body)}
</>
)
}

React. Recursive component

There is a component Child which is recursively invokes himself. Also the component has local state, for example, state = { isOpen: false }. This Child components wraps in a Parent component. So, we have one parent component with recursively generated child components (with their local state). The question is: How can I get access to specific Child component to invoke setState from the Parent component. Or how can I store recursively created components states inside Parent.
Any idea?
I did in this way:
const Asterics= ({i}) => [
<b>*</b>,
i>0 && <RAsterics i={i-1}/>
]
const RAsterics = (props)=><Asterics {...props}/>
export default Asterics
(React <16 instead)
const Asterics= ({i}) => <div>
<b>*</b>
{i>0 && <RAsterics i={i-1}/>}
</div>
const RAsterics = (props)=><Asterics {...props}/>
Then, whenever you need it:
<Asterics i={10}/>

Resources