React - Change state in child component from another child component - javascript

I need to change the state in one child component when a button is clicked in another child component. Both the childs have the same parent component.
import React from "react":
import A from "...";
import B from "...";
class App extends React.Component {
render() {
<div>
<A />
<B />
</div>
}
}
In this example, when a button in component A is pressed, the state in component B needs to be changed.

This application sounds like the perfect use case for "Lifting State Up", i.e. holding the main state in the parent component. Then you basically just pass down handlers (to change the parent state) to component A (this becomes the button's onClick handler), then pass down the state you want to show to component B.
When you click the button, the setState is called in the parent component, which automatically re-renders all children whose props change (including component B).
Here's more detailed info: https://reactjs.org/docs/lifting-state-up.html
EDIT: The reply below reminded me that I should probably add some code to illustrate - but I've made a few changes that simplify things.
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false);
function handleToggle() {
// Decouple the implementation of the parent state change from the child
// Pass a function to change the state (async/batching reasons)
setShow(show => !show);
}
return (
<div>
<A show={show} onToggle={handleToggle} />
<B show={show} onToggle={handleToggle} />
</div>
)
}
const A = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
const B = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
So basically we don't care how the state in the parent is changed. We just know that when the button in the child component is clicked, we want to trigger that change. So all we really have to do is call the function passed down via props - we don't have to pass any params.
The parent will then handle any clicks inside the handleToggle function, and you can change this implementation in the future without the child knowing anything. Perhaps you want to change to use mobx to handle state, or run some other code before finally changing the state. Since both are decoupled, you're all good! I've also adjusted setShow to use a function (benefits described here: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).

A supplement to the answer above:
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false)
return (
<div>
<A show={show} setShow={setShow} />
<B show={show} setShow={setShow} />
</div>
)
}
const A = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)
const B = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)

Related

About React Memo

I am learning React.
There are several componentized buttons.
When I click on one button, the other button components, including the parent component, are also re-rendered.
import { useState } from 'react'
const Button = ({ setState, text }) => {
console.log(`${text} rendering`)
return (<button
onClick={e => setState(prevState => !prevState)}
>
{text}
</button>)
}
const Page = () => {
console.log('Page rendering')
const [aaa, setAaa] = useState(false)
const [bbb, setBbb] = useState(false)
return (<>
<div>
<Button
setState={setAaa}
text="A button"
/>
<pre>{JSON.stringify(aaa, null, 2)}</pre>
</div>
<div>
<Button
setState={setBbb}
text="B button"
/>
<pre>{JSON.stringify(bbb, null, 2)}</pre>
</div>
</>)
}
export default Page
If anyone has more information, we would appreciate it if you could enlighten us.
Thank you in advance.
Anytime you set state in a component, the component and all of its children will re-render. You could memoize the children by wrapping them in React.memo so that if their props don't change they don't re-render. However, that will increase the memory footprint of your app, so often it only makes sense to memoize components that are expensive/slow to re-render, and your Button component is fairly simple to render.

React rerendering list

I have a react page that has several components on it with a state that shows a modal. I dont want all the components in the app to re render when the modal shows.
const CustomersPage = () => {
const [showModal, setShowModal] = useState(false);
const dataSource = [...omitted data]
return (
<>
<Modal
visible={showModal} />
<div>
<div>
<div>
<Button type="primary" onClick={() => setShowModal(true)}>
Create
</Button>
</div>
<CustomForm />
</div>
<CustomList dataSource={dataSource} />
</div>
</>
);
};
When the showModal value changes, the components CustomForm component and the CustomList component re renders but I dont want them to re render every time the modal shows because the list can have over 100 components. How can I do this?
Edit.
const CustomList = ({ dataSource }) => {
return (
<div>
{dataSource?.map(i => (
<CustomCard
id={i.id}
...other props
/>
))}
</div>
);
};
const CustomCard = ({
... props
}) => {
return (
<>
<Card
...omitted properties
</Card>
</>
);
};
You can wrap the List and Form components in a React.memo component.
This way they will only re-render when their props change their identity.
See:
https://scotch.io/tutorials/react-166-reactmemo-for-functional-components-rendering-control
You can avoid unnecessary re-rendering with memo and hooks like useMemo and useCallback if you are using FC. Or if your are in CC the your create your component pure component that prevent unnecessary render.
Make your function component memo by wrapping component with Reaact.memo, then this will help to check and render component if there is any changes down there in your this child component. Despite all hooks like useCallback and useMemo are also helpfull for optimization.
There are tons of the articles are there about the use cases of these hooks, go and have look at them. They are really helpful.
Thanks

how to pass an event from a child component to parent component back down to another child in React

Lets say for instance that I have three components in React, an App (the parent component), a button component and a component that is meant to display something, can be anything doesn't really matter. Lets say in the button component is activated, how would I pass the information (ie that the event actually happened) to the App parent component back down to the other child component to let it know a specific event happened to display some message?
this is how I would go about dong this using hooks :
const Parent=(props)=>{
[eventHappend,setEventHappend]=useState(false)
return (
<div>
<Child1 setEventHappend={setEventHappend} />
<Child2 eventHappend={eventHappend} />
</div>
)
}
const Child =({setEventHappend})=>{
return (
<div>
<button onClick={e=>setEventHappend(true)} > click me 1 </button>
</div>
)
}
const Child2 =({eventHappend})=>{
return (
<div>
<button onClick={e=>{/* some code*/ }} > {eventHappend?'event happend':'event didnt happen yet '} </button>
</div>
)
}
There are various ways you can achieve this pass state as props to the child elements (must know before other methods), context or use redux which has a store.
Generally speaking. React has one way data flow, uni directional. As in the parent will hold the state and will be passed to child elements.
Here App holds the state buttonClick which has the information about the event.
const App = () => {
const [ buttonClick, setButtonClick] = React.useState(false);
const messageToBeDispalyed = "The button has been clicked"
return (
<div>
<CustomButton setEventHappened={setButtonClick} />
<DisplayText value = {buttonClick ? messageToBeDispalyed : ""} />
</div>
)
}
const CustomButton = (props) =>{
return <button onClick={(e)=>props.setEventHappened(true)} > Click Me </button>
}
const DisplayText = (props) => {
return <h1> {props.value} </h1>
}
Similar answers to the others, but you would pass down a method to the child from the parent to update the state. But be aware that by doing this will cause a rerender for all of the parent's children.
const Parent = () => {
const [state, setState] = React.useState(false);
const handleClick = value => {
setState(value);
};
return (
<Child state={state} handleClick={handleClick} />
<OtherChild isTrue={state} /> // this component needs data changed by <Child />
)
};
const Child = props => {
const {state, handleClick} = props;
return (
<button onClick={() => handleClick(!state)} >click me</button>
);
};
This way the parent alone handles the state change and provides that method to the child.
as #Loveen Dyall and #Shruti B mentioned you can use RXJS for a more modular approach ,While RxJS is typically thought of as being used with Angular projects, it's a completely separate library that can be used with other JavaScript frameworks like React and Vue.
When using RxJS with React, the way to communicate between components is to use an Observable and a Subject (which is a type of observable), I won't go too much into the details about how observables work here since it's a big subject, but in a nutshell there are two methods that we're interested in: Observable.subscribe() and Subject.next().
learn more about RXJS and Observables : https://blog.logrocket.com/understanding-rxjs-observables/
Observable.subscribe()
The observable subscribe method is used by React components to subscribe to messages that are sent to an observable.
Subject.next()
The subject next method is used to send messages to an observable which are then sent to all React components that are subscribers (a.k.a. observers) of that observable.
here is how you implement it in this use case :
this is called a service and you would put this file in a services folder
import { Subject } from 'rxjs';
const subject = new Subject();
//here where sending { event: eventTitle } , that way you can listen to diffrent events , for example 'INCREMENTED' you could even send values
export const eventsService= {
sendEvent: eventTitle => subject.next({ title: eventTitle }),
getEventNotification: () => subject.asObservable()
};
in your Child 1 component you would subscribe to the observable in useEffect or compoentDidMount if your using class component:
import { eventsService} from '../services';
const Child1 =()=>{
const [child2EventFired,setChild2EventFired]=useState(false)
useEffect(()=>{
let subscription = eventsService.getEventNotification().subscribe(eventTitle =>
{
if (eventTitle=="CHILD2_BUTTON_CLICK" ) {
setChild2EventFired(true)
}else{
setChild2EventFired(false)
}
});
return ()=>{
subscription.unsubscribe();
}
},[])
return <div>
<button> {child2EventFired? 'child2 button event fired':'event not fired yet'} </button>
</div>
}
in your Child 2 component
import { eventsService} from '../services';
const Child2 =()=>{
Child2Click=(e)=>{
//some code,
//then send messages to the observable observable
eventsService.sendEvent('CHILD2_BUTTON_CLICK');
}
return <div>
<button onClick={Child2Click} >click me</button>
</div>
}

How does one change props value to a different variable?

The problem is I can't access the value of actuve
or change it? Is this because of the fact that
props are immutable? and if so I should I
create a separate variable for each
EventInLife element/component?
import React from 'react';
import styled,{ css } from 'styled-components';
const EventInLife = (props) => {
/*
adds the active theme to each element(example: if active then the styles
would be barbar and barbaractive if not they would be barbar and barbar)
*/
return (
<div className={`barbar barbar${ props.actuve }`}>
<div className={`square square${ props.actuve }`}></div>
<div className={`heading heading${ props.actuve }`}>{props.heading}</div>
<div className={`date date${ props.actuve }`}>{props.date}</div>
</div>
);
}
function App() {
//Manages which value is active
var lastactive=0;
function bruh(t,n){
document.getElementsByName(t)[lastactive].actuve='';
document.getElementsByName(t)[n].actuve = 'active';
lastactive=n;
}
return(
<EventInLife heading={'Top'} date={'145'} actuve={'active'} onClick={()=>bruh('EventInLife',0)}/>
<EventInLife heading={'trop'} date={'456456'} actuve={''} onClick={()=>bruh('EventInLife',1)}/>
<EventInLife heading={'brop'} date={'45646'} actuve={''} onClick={()=>bruh('EventInLife',2)}/>
<EventInLife heading={'crop'} date={'45646'} actuve={''} onClick={()=>bruh('EventInLife',3)}/>
<EventInLife heading={'wrop'} date={'145645'} actuve={''} onClick={()=>bruh('EventInLife',4)}/>
);
}
/*the css style names (i made them only overide what they needed to)*/
.barbar{}
.barbaractive{}
.squareactive{}
.squareactive{}
.headingactive{}
.headingactive{}
.dateactive{}
.dateactive{}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Yes props are immutable, think of them as arguments being passed to a function. Any values that will change over the lifecycle of a component should be stored in the state of the component, changing the state will cause the component to rerender and thus display the changes on the DOM if the state is utilized correctly. Here is a simple example where each component has a button that triggers the active state to toggle thus triggering a rerender of the component which causes the classes variable to be recomputed therefore changing the class names passed to the div element. I have made the assumption each element is initially false.
import React , {useState} from 'react';
const RandComponent = (props) => {
const [isActive , setIsActive] = useState(false);
const classes = isActive ? 'bar barActive' : 'bar';
return(
<>
<div className={classes}>hi</div>
<button onClick={() => setIsActive(!isActive)}>{isActive ? 'make me inactive' : 'make me active'}</button>
</>
);
}

How to give React parent component access to children state without form (with Sandbox)?

I'm currently looking for a way to access children state from a parent component that will handle API calls for the whole page.
The actual problem is the following:
Parent is the parent component that will render two Child components.
Each of the Child has a state that it is responsible for.
The "Kind of Submit Button" will have a "Kind of Submmit Action" (this is all quoted because this is not a form) and that should fire the function to provide access to the children state. Is there a way (some React feature) to do this without using <form> or without creating an intermediate parent component to hold all the state? I want each children to be responsible for its own state.
Code Sandbox with example of the code below
import React, { useState, useRef } from "react";
function ChildOne() {
const [childOneState, setChildOneState] = useState(false);
return (
<React.Fragment>
<h3>Child One</h3>
<p>My state is: {childOneState.toString()}</p>
<button onClick={() => setChildOneState(true)}>Change my state</button>
</React.Fragment>
);
}
function ChildTwo() {
const [childTwoState, setChildTwoState] = useState(false);
return (
<React.Fragment>
<h3>Child Two</h3>
<p>My state is: {childTwoState.toString()}</p>
<button onClick={() => setChildTwoState(true)}>Change my state</button>
</React.Fragment>
);
}
function Button(props) {
return (
<button onClick={props.kindOfSubmitAction}>Kind of Submit Button</button>
);
}
function Parent() {
const childOneState = useRef("i have no idea");
const childTwoState = useRef("ihave no idea");
function kindOfSubmitAction() {
console.log("This is the kindOfSubmit function!");
// This function would somehow get
// access to the children state and store them into the refs
return;
}
return (
<React.Fragment>
<h1>Iam Parent</h1>
<div>
<b>Child one state is: </b>
{childOneState.current}
</div>
<div>
<b>Child two state is: </b>
{childTwoState.current}{" "}
</div>
<Button kindOfSubmitAction={kindOfSubmitAction} />
<ChildOne />
<ChildTwo />
</React.Fragment>
);
}
export default Parent;
When several components need access to the same data, it's time for Lifting State Up.

Categories

Resources