Changing the state of parent component inside a child - reactjs

I have started learning react and I couldn't find the answer for one of my questions.
Let's say I have a parent component called main where I have states and one of them is a counter.
Inside main i have a child component called secondary with a few buttons.
Is that possible to change state of the parent component using the function inside the child component?
If not, would Redux help to solve that task?
Thanks in advance for clarification

Your parent component can keep a state, initialized in the constructor like follows:
class Main extends Component {
constructor (props) {
super(props)
this.state = { counter: 0 }
}
...
Then, you want to create a method in Main that increments your state counter:
incrementCounter = () => {
this.setState({ counter: this.state.counter + 1 })
}
You can pass this function reference to a child component as a prop. So, you might see this in the render method of Main:
<Child incrementParent={this.incrementCounter} />
Then, in the Child, whenever you'd like, you can call this.props.incrementParent() - and this will call the function that was passed to it from Main, which will increment the state counter for Main.
Here is a code sandbox that shows all of this put together. In it, there is a parent container (Main) with its own state counter. There is a Child component which has 3 buttons. Each button click increments its own counter, but also any button click will increment the parent's counter.
Hope this helps!

Yes, you can create a function that updates the parent state, and pass it down via props, context or redux if your particular app needs it.
E.g. Using React hooks
function Parent() {
const [value, setValue] = useState(1);
const increment = () => {
setValue(value + 1);
}
return (
<Child increment={increment} />
);
}
function Child(props) {
const { increment } = props;
return (
<button onClick={increment} />
);
}

class Parent extends Component {
state = { counter: 0 };
updateCounter = () => {
this.setState(prevState => ({
counter: prevState.counter + 1
}))
};
render() {
return (
<>
<Text>{this.state.counter}</Text>
<ChildComponent updateCounter={this.updateCounter} />
</>
);
}
}
class ChildComponent extends Component {
...
render() {
return (<Button onPress={this.props.updateCounter}><Text>{'Press Me!'}</Text></Button>)
}
}
You should pass the function of updating your counter updateCounter from parent, pass that reference to ChildComponent

You can update parent state by passing a function to the child component to update the parent.
Upon calling updateCounterfunction in the example, you can decrement or increment the value of count by supplying an operator in the function's argument which can be plus or minus
class Parent extends Component {
state = {
count: 1
};
updateCounter = operator => {
if (["plus", "minus"].includes(operator)) {
this.state(prevState => ({
count: prevState.count + (operator === "plus" ? 1 : operator === "minus" ? -1 : 0)
}));
}
}
render() {
const { count } = this.state;
return (
<Child count={count} updateCounter={this.updateCounter} />
);
};
};
const Child = props => (
<div>
<button onClick={() => props.updateCounter("minus")}>MINUS</button>
<button onClick={() => props.updateCounter("plus")}>PLUS</button>
<h1>{props.count}</h1>
</div>
);

Related

Higher Order Components - React.JS

Trying to pass initial value 10 to the Higher Order Components.
import HOC from './HighOrderComponent'
class ClickHandler extends Component{
state= {val : 10}
increment = () => {
this.props.increment(this.state.val)
}
render(){
return <button onMouseOver={this.increment}>Mouse Over {this.props.value}</button>
}
}
export default HOC(ClickHandler)
Here, I do not know how to use the value to update the state of
const HOC = (OriginalComponent) => {
class HigherOrderComponent extends Component{
state = { count : 0 }
increment = (value) => {
this.setState(prev => ( {count : prev.count + 1 }))
}
render(){
return <OriginalComponent value={this.state.count} increment={this.increment}/>
}}
return HigherOrderComponent
}
export default HOC
Expected Result: On load, Mover Over 10, once hovered, it will increment to 11, 12, 13.....
thanks.
The issue with your components is that HOC component's state overrode your 10 state defined in your ClickHandler.
If you changed state = { count : 0 } to state = { count : 10 } you will get the expected initial value of 10.
But seeing the code, what is the purpose of having state value maintained in both components? And the state value of ClickHandler is not even used by the HOC.
Guessing you want to maintain the state within HOC and simply wanted to pass in an initial value for it.
Would suggest the following changes:
class ClickHandler extends Component{
// no need to maintain state again in `ClickHandler`
// simply use the increment props for onMouseOver
render(){
return <button onMouseOver={this.props.increment}>Mouse Over {this.props.value}</button>
}
}
// add extra param for HOC to pass in initialValue of 10
export default HOC(ClickHandler, 10);
const HOC = (OriginalComponent, initialValue) => {
class HigherOrderComponent extends Component{
state = { count : initialValue }
// no need to get the state value from `ClickHandler`
increment = () => {
this.setState(prev => ( {count : prev.count + 1 }))
}
render(){
return <OriginalComponent value={this.state.count} increment={this.increment}/>
}}
return HigherOrderComponent
}

React and React Hooks: Using an onClick function in a child to fire a function of a parent wrapping component

I have a wrapper component that conditionally renders it's children based on it's own state (isDeleted). Basically I have a 'delete-able item' component where if a button is clicked to delete, the item itself will be removed from the DOM (by returning an empty ReactNode i.e. <></>). The problem is, I can't figure out how to have the button click event, which appears as a child of the wrapper, to be passed INTO the wrapped component itself:
export default function App() {
return (
<DeleteableItemComponent>
{/* lots of other markup here */}
<button onClick={triggerFunctionInsideDeletableItemComponent}>
</DeleteableItemComponent>
)
}
and the most basic version of my delete-able item component:
export default function DeleteableItemComponent() {
const [isDeleted, setIsDeleted] = useState(false);
const functionIWantToFire = () => {
// call API here to delete the item serverside; when successful, 'delete' on frontend
setIsDeleted(true)
}
if (isDeleted) {
return <></>
}
return <>{children}</>
}
So put very simply, I just want to call the functionIWantToFire from the button onClick callback.
How can this be done properly via hooks? I've thought of using the context API but I've never seen it used to trigger function firing, only for setting values, and in this case I want to fire the event itself, not communicate specific values to the wrapper component. I also can't do it correctly through just passing a boolean prop, because then I can only set it once i.e. from false to true.
You could use React.cloneElement API to pass props to your child while iterating through it using React.children.map.
React.Children.map(children, (child) => {
return React.cloneElement(child, { /* .. props here */ });
});
A simple example would be.
You could check the example here
function App() {
return (
<Delete>
<Child1 />
<Child2 />
</Delete>
);
}
function Delete({ children }) {
const [clicked, setClicked] = React.useState(0);
const inc = () => setClicked(clicked + 1);
const dec = () => setClicked(clicked - 1);
return React.Children.map(children, (child) => {
return React.cloneElement(child, { inc, clicked, dec });
});
}
function Child1(props) {
return (
<div>
<p>{props.clicked}</p>
<button onClick={props.inc}>Inc</button>
</div>
)
}
function Child2(props) {
return (
<div>
<button onClick={props.dec}>Dec</button>
</div>
)
}

React.js: how to set an active flag in one of multiple identical children?

I have a parent component with multiple identical children of which only one can be active at a time. The active state is to be set through an internal event on the child itself e.g. a button click, and not by the parent. I want the active state to be unset by a call from a sibling but I cant find a way for siblings to call eachother's methods. Ive tried refs but they are only accessible from the parent and i cant find a way to make a child ref available within itself without maintaining a list of refs on the parent which i dont want as i only need to store the currently active one.
Simple example
e.g.
<Parent>
<Child active={false}/>
<Child active={false}/>
<Child active={true}/>
</Parent>
where a child is something like
export class Child extends React.Component {
constructor() {
super(props);
state = {
active: props.active;
}
}
setActive(active) {
setState ({active : active});
}
onclick = () => {
// get currently active other sibling?
// call setActive direct on other sibling.
// e.g. other.setActive(false);
// set current to active using the same method
this.setActive(true);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.state.active ? "active": "inactive"}
);
}
}
I've tried passing in parent setActiveRef and getActiveRef functions as props to the children to maintain a single shared ref (the active one) and use getActiveRef().current.setActive directly but i cant find a way to access the ref of a child component from within itself to send to the setActiveRef() on the parent in the first place.
any advice much appreciated. thanks
In short, this isn't how React is designed - children won't be able to modify each other's state directly. A simple analogy would be a literal parent and their children. You want the children to know when it's time to raise their hand, but they don't take directions from each other, only from Mom or Dad. What you CAN do is tell the kids how to communicate with their parents, and let their parents deal with it.
I'm sure there are better ways, but here is some code:
export class Parent extends React.Component {
constructor() {
super(props);
state = {
activeChild: "Jimmy"
}
};
// Let the parents listen for requests
handleChildRequest (name) => {
this.setState({activeChild: name});
};
render() {
<div>
<Child active={this.state.activeChild === "Jimmy"} name="Jimmy" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Sally"} name="Sally" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Fred"} name="Fred" handleRequest={this.handleChildRequest} />
</div>
};
}
export class Child extends React.Component {
constructor() {
super(props);
// Lets forget about local state, we don't need it!
}
onclick = () => {
this.props.handleRequest(this.props.name);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.props.active ? "active": "inactive"}
);
}
}
Answer to my own question using component passing (this) references to parent callback. This is not complete code but i illustrates the point. Appears to work fine for my use case (updating realtime map locations) which is more complex than this simplified example.
parent component passes callbacks to children to store ref to active component
export class Parent extends React.Component {
activeChild = undefined;
setActiveChild = (child) => {
activeChild = child;
}
getActiveChild = () => {
return activeChild;
}
// set up some callback props on each child
render() {
return (
<Parent>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={true} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
</Parent>
)
}
each child simply calls back on the parent using prop callbacks and passes itself. this allows the state to be set internally within the component forcing a re-render if values change.
export class Child extends React.Component {
onclick = () => {
// get currently active other sibling stored in parent
let active = this.props.getActiveChild();
// call setActive directly on other sibling.
active.setActive(false);
// store currently active child in parent
this.props.setActiveChild(this);
// set 'this' component to active using the same method
this.setActive(true);
}}
criticisms and improvements most welcome.
thanks
I was looking for a way to do something similar with hooks, and this is what worked for me:
import React, { useState } from 'react';
const Parent = () => {
const [activeChild, setActiveChild] = useState(undefined);
return (
// Now the children have access to the current active child
// and also the ability to change that
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'1'}/>
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'2'}/>
)
export default Parent
then inside the Child component...
import React, { useState, useEffect } from 'react';
const Child = ({activeChild, setActiveChild, id}) => {
const [activated, setActivated] = useState(false);
useEffect(() => {
if (activeChild !== id) {
setActivated(false);
}
}, [activeChild, chapter]);
return (
//on click the component will be able to be activated and set itself as the activated component.
<div onClick={()=> {
setActivated(true);
setActiveChild(id)
}} />
)
export default Child
The useEffect in the Child will check if its id (which is unique to itself) is the id of the activeChild. If not, it'll make sure that its local state of activated is false.
Once activated though, it'll set its local state of activated to true, and set the activeChild's id to its own id.
any feedback is welcome!! This made sense in my head.
Let's supposed you have an array of childs components, each one of those child, will have a prop called active, so you could use an state variable to store the array of childs, so when one of the childs gets updated, and cause a rerender of each one of the child components as well.
import React from "react";
const Parent = () => {
const childs = [{ active: false }, { active: false }, { active: false }];
const [childrens, setChildrens] = React.useState(childs);
const onOptionSelected = idx => {
setChildrens(prevOptions =>
prevOptions.map((opt, id) => {
opt.active = id === idx;
return opt;
})
);
};
return (
<div>
{childrens.map((child, id) => {
return (
<Child
key={id}
id={id}
active={child.active}
onOptionSelected={onOptionSelected}
/>
);
})}
</div>
);
};
const Child = ({ id, active, onOptionSelected }) => {
console.log(id)
const onClick = () => {
onOptionSelected(id);
};
return (
<>
<button onClick={onClick}>set active</button>
<p>current state={active ? "active" : "inactive"}</p>
</>
);
};
export default Parent;

How to call a child method on parent events with react?

I have a child component that need to listen to one of it's parent event. More precisely, I have a function in the child component that takes as parameter an event from the parent. I would like to call this function everytime the event occurs.
As an example, here is a code snippet:
class Parent extends React.Component {
handleKeyDown = (event) => {
// Call the child function doSomething()
}
render() {
return (
<input
type="text"
onKeyDown={this.handleKeyDown}
>
<Child />
)
}
}
class Child extends React.Component {
doSomething = (event) => {
// Get the event from parent
}
render() {
return (
...
)
}
}
I have considered two ways to do it:
Using ref to call the child function from the parent onKeyDown (supposing that I can access it)
Using a state to store the event and pass it as a props to the child, then listen to props changes with getDerivedStateFromProps.
However, none of these solutions seems very appealing. I have also thought about using a redux function but I need data from the child component as well as the event from the parent component... I was wondering if there is a clean way do to that?
Update:
I updated my components to use hooks and ended up using useRef(), useImperativeHandle() and forwardRef() to handle this case:
const Parent = () => {
const childRef = useRef();
const handleKeyDown = (event) => {
// Call the child function doSomething()
childRef.current.doSomething(event);
};
return (
<input
type="text"
onKeyDown={handleKeyDown}
>
<Child ref={childRef} />
);
};
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
doSomething: (event) => {
// Get the event from parent
}
}));
return (
[...]
);
});
I decided to use the solution provided by Francis Malloch on this post1:
class Parent extends React.Component {
childCallables = null;
setChildCallables = (callables) => {
this.childCallables = callables;
}
handleKeyDown = (event) => {
// Call the child function doSomething()
this.childCallables.doSomething(event);
}
render() {
return (
<input
type="text"
onKeyDown={this.handleKeyDown}
>
<Child setCallables={this.setChildCallables} />
)
}
}
class Child extends React.Component {
componentDidMount() {
this.props.setCallables({
doSomething: this.doSomething
});
}
doSomething = (event) => {
// Get the event from parent
}
render() {
return (
[...]
)
}
}
Basically, I'm using a props to store the child's methods I need to access from the parent. The methods are saved in the props just after the child component is mounted.
1. Since it is an answer to a completely different question, I don't think marking this one as a duplicate would make sense.
You can write a HOC like this:
const withChild = Wrapped => class Child extends React.Component {
doSomething = (event) => {
}
render() {
return (
<React.Fragment>
<Wrapped {...this.props} onKeyDown={this.doSomething}/>
whatever Child should render
</React.Fragment>
)
}
}
const ParentWithChild = withChild(class Parent extends React.Component {
handleKeyDown = (event) => {
// Call the child function doSomething()
if (typeof(this.props.onKeyDown) === 'function') {
this.props.onKeyDown(event);
}
}
render() {
return (
<input
type="text"
onKeyDown={this.handleKeyDown}
>
)
}
});
Try calling doSomething in render method before returning on the basis of props changed but this will result in an infinite loop in case you are changing the state of child component in doSomething.

Children ref undefined react native

I created a component wrapper around ViewPagerAndroid (simplified version)
class TabView extends Component {
constructor(props){
super(props)
this.state = { position: 0 }
}
changePage = (key) => {
this._pagerRef.setPage(key)
this.setState({position: key})
}
render(){
return(
<ViewPagerAndroid ref={(ref) => this._pagerRef = ref}>
{ this.props.scenes }
</ViewPagerAndroid>
)
}
}
I want to trigger changePage from outside the component (eg from: <TabView ref={(ref) => this._ref = ref} />, and run this._ref.changePage(key)).
However, each time I try to do so, this._pagerRef is undefined inside the changePage function of TabView.
What am I missing ?
There is a more idiomatic React solution to the problem you are trying to solve -- namely making TabView a controlled component and setting ViewPager page on componentDidUpdate:
class TabView extends Component {
componentDidUpdate = ({ page }) => {
// call setPage if page has changed
if (page !== this.props.page && this._pagerRef) {
this._pagerRef.setPage(page);
}
};
render() {
return (
<ViewPagerAndroid
initialPage={this.props.page}
ref={ref => this._pagerRef = ref}
onPageSelected={e => this.props.pageChanged(e.nativeEvent.position)}
>
{this.props.scenes}
</ViewPagerAndroid>
);
}
}
You can then move the current page tracking to the parent component's state and pass it down to TabView as a prop, along with a handler that updates it when the value changes:
render() {
return (
<TabView
page={this.state.page}
pageChanged={page => this.setState({page})}
/>
)
}
You're trying to access the ref from outside of the component which has no instance to it.
Therefore you need to pass it as a prop from the parent component itself. Also you need to move the changePage to the parent component to access it from outside.
Parent
changePage = (key) => { //... Call the function here
this._pagerRef.setPage(key)
this.setState({position: key})
}
accessRef (ref) {
this._pagerRef = ref . //... Bind the ref here
}
<TabView passRef={this.accessRef} /> //...Pass the ref here
Child
<ViewPagerAndroid ref={this.props.passRef}> . // ... Set the ref here
{ this.props.scenes }
</ViewPagerAndroid>

Resources