How can show and hide react components using hooks - reactjs

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.

Related

In React, does a list of memo component get rendered if they are inside the map function called from the a list state?

Assuming I have a memoized child component:
function Child({number}){
return <div> {number} </div>;
}
export React.memo(child)
The Child component only gets rerender if the number props is changed.
Now, I put the Child component inside the Parent component. The parent component is as of following:
function Parents(){
const [numbers, setNumbers] = useState([1,2,3,4,5,6]);
return (
<>
numbers.map((number,index)=>{
<Child key={index} number={number}>
}
</>
)
}
So, in this case, the Child components are generated inside a loop over the numbers array. However, the child components are memoized. In case when only one number is changed in the array, for example, if we change [1,2,3,4,5,6] to [1,2,111111,4,5,6]. The third number goes from 3 to 111111. Now, does the every child element get rerendered, or only the third child elements get rerender?
In this case, only the third child will be re-rendered, as the props for the other children will be the same
You can check this for yourself using the following example:
import {memo, useState} from 'react'
const Child = memo(({number}) => {
console.log('re-render', number);
return <div>{number}</div>;
})
export default function App() {
const [numbers, setNumbers] = useState([1,2,3,4,5,6]);
return (
<>
{numbers.map((number,index)=>
<Child number={number} /> )
}
<button onClick={() => {
setNumbers(prev => {
prev[2] = 111;
return [...prev];
})
}}>
click
</button>
</>
)
}

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 Re-render Child-Component for New Updated Props Value

I am trying to update the Progress and Steps components from Ant-Design UI with new props value as shown below:
import React from 'react';
import { Progress, Steps } from 'antd'
const Step = Steps.Step
const Parent = ({ a }) => {
const haveCompleted = () => {
var intr = setInterval(() => {
if(c < 754) {
console.log(c)
return c++
}else {
clearInterval(intr)
}
}, 300)
}
return (
<React.Fragement>
<div>
<Steps> />
<Step description={a !== null ? haveCompleted() : null} ... />
<Step ... />
</Steps>
</div>
<div>
<Progress percent={haveCompleted()/754*100 || 0} ... />
</div>
</React.Fragement>
)
}
export default Parent;
On logging to browser console the value gets updated in function but doesn't get reflected in components.
What am I doing wrong?
You are not updating the state anywhere. You are just executing an inline function. The state should be passed down to the component and re-render the affected child or your component should hold state. As you can see from the Ant documentation there should be a current prop on Steps which you have not included. Finally the description prop is of type string | ReactNode and your function is returning neither.
Worth-noticing mistake as pointed out by helpful folks above:
Not using state to render the component
Solution:
Changing parent component to Class-based
Adding State called c
Calling haveCompleted function inside ComponentDidUpdate lifecycle method to update State
Passing state c directly to child component

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.

Is there a way to access a React component's sub-components?

So I know that you can access a component's children with this.props.children:
<MyComponent>
<span>Bob</span>
<span>Sally</span>
</MyComponent>
Which is great if I'm interested in Bob and Sally, but what if I want to interact with the components that make up MyComponent (i.e. Subcomp1 and Subcomp2 shown below)?
render: function() {
return (
<div className="my-comp">
<Subcomp1 />
<Subcomp2 />
</div>
);
},
Use Case
I'm trying to create a higher order component that manages the tab index (roving tab index: https://www.w3.org/TR/wai-aria-practices/#kbd_roving_tabindex) of the wrapped component's sub-components, so it would be great if I could get a ref to the wrapped component and filter it's subcomponents by type.
So far the only approach that seems possible is to have each component store a ref for each of it's subcomponents, but this is tedious and kind of defeats the purpose of an HOC. Is there a generic way to access these sub-components?
A rough example of what I'm trying to do:
var HOC = (ComposedComponent) => {
return React.createClass({
componentDidMount: function() {
const subComponents = this.composedComponent.subComponents; // Something like this would be nice
const menuItems = subComponents.filter(() => {
// figure out a way to identify components of a certain type
});
this.applyRovingTabIndex(menuItems);
},
render: function() {
return (
<ComposedComponent
ref={(c) => { this.composedComponent = c }}
{...this.props} />
);
}
});
};
The tabIndex manipulation need not be done in the HOC, rather it can be done in the Parent component that renders all the HOCs. Because all you need is to determine which sub component is clicked and adjust the selected state on the Parent component. This selected state can then be propagated back to the sub components who compare their index with selected index and assign tabIndex accordingly.
You can send the respective props to determine whether the current ComposedComponent is selected or not by passing an onClick event handler all the way. Then in your sub component you can access tabIndex using this.props.tabIndex and render your parent div as
<div tabIndex={this.props.tabIndex}> </div>
The code below is almost like pseudo code to give an idea. If you feel that this does not solve your requirement you can try out a Tab example worked out by an awesome developer at this link CODEPEN EXAMPLE
const HOC = (ComposedComponent) => {
return class extends React.Component {
render (
<ComposedComponent
tabIndex={this.props.selected === this.props.index ? "0" : "-1"}
{...this.props}
/>
)
}
}
class Parent extends React.Component {
state = {
selected: 0
}
// Set the current selection based on the currentSelection argument
// that is bound to the function as it is sent along to Props
adjustTabIndices = (currentSelection) => (event) => {
this.setState({selection: currentSelection})
}
render {
return (
<div>
{
// These are your various MenuItem components that
// you want to compose using HOC
[MenuItem1, MenuItem2, MenuItem3].map(index => {
const MenuItem = HOC(MenuItem1);
return (
<MenuItem
key={index}
onClick={this.adjustTabIndices(index)}
selection={this.state.selected}
index={index}
/>
)
})
}
</div>
)
}
}

Resources