react: update state in child component at real-time - reactjs

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.

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>

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

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

React load in component onClick

I'm trying to load in a component when a button is clicked but when I click on the button () in the below code nothing appears to be happening. I'm just trying to display a copied message and then have it disappear shortly after it appears to show the user the selected text was copied to their clipboard.
This is my current code:
import React, { useState } from 'react'
import Clipboard from 'react-clipboard.js';
const AddComponent = () => {
console.log("copied")
return (
<p className="copied">copied to clipboard!</p>
)
};
export default function Item(props) {
const { itemImg, itemName } = props
return (
<>
<Clipboard data-clipboard-text={itemName} onClick={AddComponent} className="item-container display-flex">
<img src={itemImg} alt={itemName} className="item-img" />
<h3>{itemName}</h3>
</Clipboard>
{AddComponent}
</>
)
}
mostly you want to have a state control, to conditionally render the given component like { isTrue && <MyComponent /> }. && operator only evaluates <MyComponent /> if isTrue has truthy value. isTrue is some state that you can control and change to display MyComponent.
in your case your onClick should be responsible to control the state value:
import React, { useState } from 'react'
export default function Item(props) {
const { itemImg, itemName } = props
const [isCopied, setIsCopied] = useState(false)
const onCopy = () => {
setIsCopied(true)
setTimeout(() => {
setIsCopied(false)
}, 600)
}
return (
<>
<div data-clipboard-text={itemName} onClick={onCopy} className="item-container display-flex">
<img src={itemImg} alt={itemName} className="item-img" />
<h3>bua</h3>
</div>
{isCopied && <AddComponent/>} // short circuit to conditional render
</>
)
}
you could consider check the repo react-toastify that implements Toast messages for you.
You'll want to have onClick be a regular function instead of a functional component, and in the regular function implement some logic to update the state of Item to record that the Clipboard was clicked. Then in Item, instead of always including <AddComponent />, only include it based on the state of Item.

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.

Resources