How to change state of parent from nested child in React Hooks? - reactjs

I have 3 nested components in my React app.
1.Parent calling Child1
2.Child1 calling Child2
3.Child2 calling Child3
4.Child3
I want to show a Dialog pop up when user click a button.
The Dialog is in parent and I show it by setting a state variable
const[showDialog, setShowDialog] = useState(false);
true/false when user click on the ShowDialog Button
The problem is the button is in Child3.
So how I can make the state change in parent when I click the button in the last child.

use useContext ,useReducer hook, instead of passing props to the children, you can use useContext, useReducer hook to dispatch the action by onclick in the parent and get the true or false response from the context to the chlld component. here is the codeSandbox exmaple

Step 1 - Create a handler function in parent i.e which can toggle the showDialogue state to true or false
const showDialogHandler=()=>{
setShowDialog(prevState=> !prevstate)
}
Step 2: pass the handler reference to the child3
<Child3 onShowDialog={showDialogHandler} />
Step3 : assign that function passed through prop to onClick vent handler.
<button onClick={props.onShowDailog}>Show Dialog</button>

// Parent.js
const Parent = () => {
const [showDialog, setShowDialog] = useState(false);
return (
<>
<div>Show Dialog: {showDialog.toString()}</div>
<Child3 showDialog={showDialog} setShowDialog={setShowDialog} />
</>
);
};
// Child3.js
const Child3 = ({showDialog, setShowDialog}) => {
const handleShowDialog = useCallback(() => {
setShowDialog(!showDialog)
}, [setShowDialog, showDialog])
return (
<>
<button onClick={handleShowDialog}>Show Dialog</button>
</>
);
};

Related

Reset functional child components from parent

I need to rerender the components GridSquare from component GridPixels. When the users clicks the button "Reset Colors" the component should reset the child components generated by a map.
The flow is:
User clicks "Reset Colors" button. This button is inside GridPixels component.
Gridpixels component should rerender.
The GridSquare component should be reset. This means that his state should be reset. The purpose of this is that inside GridSquare there is a css class called "set-color-red". When resetting the GridSquare component, the state inside GridSquare component should contain "".
All the GridSquare components are rerendered but the state is mantained. I need to reset the state for every GridSquare component from the GridPixels component.
I tried adding one to the index map each time the "Reset Colors" button is clicked but the state is conserved, is not reset.
import { useState } from 'react';
import GridSquare from './GridSquare'
function GridPixels(props) {
var foo = new Array(192).fill(0);
const [checked, setChecked] = useState(false);
const toggleChecked = () => setChecked(value => !value);
function reset(){
toggleChecked() //This is for rerender the GridSquare component. It doesn't work.
}
return (
<div className="grid-container">
<div className="grid">
{foo.map((item, index) => {
console.log(checked)
return <GridSquare key={ index } />
})
}
</div>
<div><button onClick={reset}> </button></div>//Reset button Colors
</div>
)
}
export default GridPixels
import { useState } from "react";
function GridSquare(props) {
const [a, setA] = useState("");
const changeStyle = () => {
if (a === "set-color-red") {
setA("")
return
}
setA("set-color-red");
}
return <div onClick={changeStyle} className={a}></div>
}
export default GridSquare
Edit: I was able to do what I asking for with the following javascript code:
function reset(){
var classesToRemove = document.querySelectorAll(".set-color-red")
classesToRemove.forEach((item) => {
item.classList.remove("set-color-red")
})
}
I post this to generate a better idea of what I am trying to do.
Edit2: Here is a sandbox of what I am trying to do. There is a grid, and when you click an square, it changes color. There is a reset button, at the right of the grid, to clear all colors from the squares. This is the functionality I can't do.
Sandbox with the code
You can use a key prop on your parent.
The special key prop is used by React to help it understand which components are to be rerendered with prop changes and which should be scrapped and rebuilt.
We run into this most often when mapping over something to build a list.
Pass a callback down to your children that will update the value of key.
See the forked sandbox: https://codesandbox.io/s/react-functional-component-forked-vlxm5
Here are the docs: https://reactjs.org/docs/lists-and-keys.html#keys
const App = (props) => {
const [key, setKey] = useState(nanoid());
return (
<div>
<GridPixels key={key} reset={() => setKey(nanoid())} />
</div>
);
};
// then in your grid component
// ...
<button onClick={props.reset}> Reset Colors</button>

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>
)
}

How can show and hide react components using hooks

Hy! How can i display a component if another component is visible, ex
if component 1: show
component 2: hide
component 3: hide
if component 2: show
component 3: hide
component 1: hide
(i have 10 components)
Im using react hooks, thanks
You need to use useEffect hook to track the open state of the component which you want to sync with another component.
Next code will trigger the opening of the Comp2 component while Comp1 is opened
function Comp1({open, showAnotherChild}) {
useEffect(() => {
if (open) {
showAnotherChild()
}
}, [open])
if (!open) {
return null
}
return // markup
}
function function Comp2({open}) {
if (!open) {
return null
}
return // markup
}
function Parent() {
const [comp1Open, setComp1Open] = useState(false)
const [comp2Open, setComp2Open] = useState(false)
return (
<>
<Comp1 open={comp1Open} showAnotherChild={setComp2Open} />
<Comp2 open={comp2Open} />
<button onClick={() => setComp1Open(true)}>Open Comp1</button>
</>
)
}
You need to handle this in a parent component, the parent for your 10 children. This parent component should implement the logic driving the hidden/shown state for all the children.
In other words you need to lift state up.
You can keep one string value in useState which will be id for the component in this case it will be only one state value through which we will toggle display. You can see it below
function Parent() {
const [childToDisplay, setChildToDisplay] = useState(null)
return (
<>
<Comp1 id='comp-1' display={childToDisplay === 'comp-1'} />
<Comp2 id='comp-2' display={childToDisplay === 'comp-2'} />
</>
)
}
To toggle the display you can keep button in parent component. Whenever you have to show any component you can set the correct id to state as string and it will then display child component accordingly.
This way you don't have to set multiple boolean state values for multiple child components.

How to pass state value as a prop and set it in child?

I have a child component where I am passing state from the parent, and then I want to update the parents state.
I call the child in my parent component like this:
<ReminderButton
buttonDisabledState={this.state.oneDayButtonDisabled}
addReminderFunction={this.props.defaultHomeworkReminder(item, 'oneDay', 1)}
/>
Then in the child it updates state on the button press like this:
onPress={() => {
this.setState({ [props.buttonDisabledState]: true });
setTimeout(() => this.setState({ [props.buttonDisabledState]: false }), 1500);
if (props.isReminderActive === false && moment(new Date()).isBefore(props.item.date)) {
props.addReminderFunction();
}
}
I am receiving an error that the setState is undefined.
Create a function in your parent component to update the state because your parent component handles the state.
handlePress = value => {
this.setState({ buttonDisabledState: value });
}
Pass this function as a prop to the child component
<ReminderButton
pressHandler={this.handlePress}
addReminderFunction={this.props.defaultHomeworkReminder(item, 'oneDay', 1)}
/>
And in your child component, use it like this
onPress={() => {
props.pressHandler(true);
setTimeout(() => props.pressHandler(false), 1500);
if (props.isReminderActive === false && moment(new Date()).isBefore(props.item.date)) {
props.addReminderFunction();
}
}
Do NOTE that the setState() calls are asynchronous and it might happen that you use the state's new value (thinking that you have called setState()) without it being actually updated which may cause not very easily identifiable problems.
You can´t set the state of the parent directly in the child component, but you can give the child component a callback function as a property:
<ReminderButton
buttonDisabledState={this.state.oneDayButtonDisabled}
updateParentState={this.onSetStateFromChildComponent.bind(this)}
addReminderFunction={this.props.defaultHomeworkReminder(item, 'oneDay', 1)}
/>
And then the function in the parent element:
public onSetStateFromChildComponent(newState){
this.setState(() => newState);
}
Greetings!
Ok react per default permits pass data or state from parents to child components if you need to make from child to parent there are some options you can use maybe Redux or Mobx to manage the state, but if you don´t install any third party library, you must use context API or use functions to handle this, this is a little example
We have a parent component that wraps a counter component and has a prop called "changeScore" that receive a function handleScore, (handle score change the state incrementing the state by one)
state = {counter: 0};
handleScore(i) {
this.setState(state => ({
counter: state.counter + i
}))
};
render(){
return (
<div className="scoreboard">
<Counter
changeScore={this.handleScore}
/>
</div>
)
}
And here is the Counter component that use ChangeScore prop to shot it when the user make click on the button
const Counter = ({changeScore})=>{
return(
<div className="counter">
<button
onClick={
()=>{changeScore(1)}
}
>+</button>
</div>
);};

react: update state in child component at real-time

I want to open dialog (another component) if the user clicks on a button in the first component.
my components are written as a function
First component:
function Bookings(props) {
const [dialog, setDialog] = useState(false);
function openUserDialog() {
console.log("called")
setDialog(true)
}
return (
<div>
<button onClick={openUserDialog}>open dialog</button>
<BookingsUserDialog isOpen={dialog }/>
</div>
);
}
export default (Bookings);
Secont Component (dialog):
function BookingsUserDialog(props) {
const [isDialogOpened, setDialogOpened] = useState(false);
let open = props.isOpen;
if (open){
console.log("should open dialog")
// handleDialogOpen();
}
function handleDialogOpen() {
setDialogOpened(true);
}
function handleDialogClose() {
setDialogOpened(false);
}
return (
<div>
<Dialog
fullWidth={true}
maxWidth={"xs"}
open={isDialogOpened}
onClose={handleDialogClose}
TransitionComponent={Transition}
aria-labelledby="booking-dialog">
some text
</Dialog>
</div>
);
}
export default (BookingsUserDialog);
if I called handleDialogOpen in BookingsUserDialog ,I get
Too many re-renders. React limits the number of renders to prevent an
infinite loop.
so is the right way to update the state in the child component?
what I want to do is open a dialog when the user clicks on a list item.
You're better off letting your parent component handle whether the dialog is open or closed, the child component doesn't need its own state, it can just use the isOpen prop. You can pass the setDialog function down to it as a prop as well and then call that with false in the dialog's handleDialogClose function.

Resources