Adding refs to components passed in an array - javascript

I'm trying to make a component that takes a list of other components as a prop and then renders one of those components as a child according to an index held in the parent's state.
Ultimately I want to be able to call the 'getValidation' function of a child element in the array using imperative handler and forwardRef methodology. I've done this for a single child component but can't figure out how to do it with an array of children. I thought about creating an array of refs in the parent component but couldn't get that right. Would appreciate any help and alternative ways of going about this are more than welcome.
E.g.
Parent:
import React, {createRef, useRef, useEffect, useState} from 'react';
const Parent = props => {
const [currentChildIndex, setCurrentChildIndex] = useState(0);
return (
<div className='parent'>
{
props.children[currentChildIndex]
}
</div>
)
};
export default Parent;
Child:
import React, {forwardRef, useEffect, useImperativeHandle, useRef} from 'react';
const Child = forwardRef((props, ref) => {
isValidated() {
//stuff that validates the form
return true;
}
useImperativeHandle(ref, () => ({
getValidation() {
return isValidated();
}
}));
return (
<div className='child'>
{form with inputs and things}
</div>
)
});
export default Child;

So I'm not sure if this is the best way to do it but I was having trouble asigning the refs to my array from inside the render method of the Parent component so I made a function to render the child component and called that from the render method and it seems to be working:
import React, {createRef, useRef, useEffect, useState} from 'react';
const Parent = props => {
const [currentChildIndex, setCurrentChildIndex] = useState(0);
function renderChildComponent(CurrentComponent) {
return (
<CurrentComponent ref={childRefs[currentChildIndex] = createRef()} />
)
}
return (
<div className='parent'>
{
renderChildComponent(props.children[currentChildIndex])
}
</div>
)
};
export default Parent;

Related

why role base parent of two child component have different state if update in one of these child component in reactjs

I want to know I have one parent component and two child components and these child components are separated according to the user role. I have passed the parent state in these child components. In the beginning, both child components have the same state value, but if I update the state value in one child component, it will not update the state value in another component why.
Here is an example code.
import React, { useEffect, useState } from "react";
import Demo1 from "./Demo1";
import Demo2 from "./Demo2";
const Demo = () => {
const [staVal, setStaVal] = useState("hi");
console.log(staVal);
const user = JSON.parse(localStorage.getItem("auth"));
return (
<div>
{user.role === "user" ? (
<Demo1 staVal={staVal} handler={() => setStaVal("google")} />
) : (
<Demo2 staVal={staVal} />
)}
</div>
);
};
export default Demo;
Demo1 component:
import React from "react";
const Demo1 = ({ staVal, setStaVal, handler }) => {
return (
<>
<div>demo1:{staVal}</div>
<button onClick={handler}>clik</button>
</>
);
};
export default Demo1;
Demo 2 component:
import React from "react";
const Demo2 = ({ staVal }) => {
return <div>demo2:{staVal}</div>;
};
export default Demo2;
Accessing localStorage is a side effect.
Side effects cannot be called from the render method (for Class components) or the top level (function components).
In your code, access the localStorage inside useEffect(()=>{}, []) or
inside componentDidMount if you want to make it a class component.
use the useEffect to get the item from the local storage.
const [user,setUser]=useState(null);
useEffect(()=>{
const currentUser = JSON.parse(localStorage.getItem("auth"));
setUser(currentUser)
},[])
return (
<div>
{user.role === "user" ? (
<Demo1 staVal={staVal} handler={() => setStaVal("google")} />
) : (
<Demo2 staVal={staVal} />
)}
</div>
);
};

react accordion component collapse in another component

I have two component in my project. I have below component in my 1st component
import React, { useContext, useState, useEffect, useMemo, useRef } from "react";
function Invoice() {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.click();
}
}
2nd component
import React, { useContext, useState, useEffect, useMemo, useRef } from "react";
function Modal() {
toggleAccordion ();
}
}
I need to call 'toggleAccordion()' function inside the second component. how i do it
There are different ways to do it.
If one component is inside another component then you can pass reference of function to child component and call from there.
If components are not nested then you can use react context
1- Pass function as props
import React, { useContext, useState, useEffect, useMemo, useRef } from "react";
function Invoice() {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.click();
}
return (<Modal toggle={toggleAccordion }/>);
}
import React, { useContext, useState, useEffect, useMemo, useRef } from "react";
function Modal({toggle}) {
toggle();
}
}
2- Configure store (Context API or Redux)
checkout this open-sourced repository which shows both ways, see here
You can manage your components with props which are the part of app state. In case you change something inside you child component depending on events or data in the parent one you should pass the proper props:
const Child = ({color}) => <p style={{"color":color}}>{color}</p>
const Parent = () => {
const [childColor, setChildColor] = useState("#faa")
return <Child color={childColor} />
}
Finally, you may also send you function from the parent component as the prop
In the example above you utilize the prop "color" just to show the data. But also you can check the new value and decide what should be done further with useEffect hook help:
const Child = ({color}) => {
useEffect(()=>{
switch (color) {
case "#fff":
console.log('the color is white')
break
case "#000":
console.log('the color is black')
break
default:
console.log(`the color's code is ${color}`)
}
}, [color])
<p style={{"color":color}}>{color}</p>
}
Here you catch the new data value and make a decision whether anything should be done or not. You can call any functions, set up className etc, but the main idea s all these should be based on state / props change.
You can also define function in you parent component and then send it to the child component as prop:
const Child = ({func}) => {
func("Hi")
return null
}
const Parent = () => {
const func = (str) => {
console.log(str)
}
return <Child func={func} />
}

Custom hook's state does not update across all components?

import { useState } from 'react';
export default function usePrivacyMode() {
const [isPrivacyOn, setIsPrivacyOn] = useState(false);
return {
isPrivacyOn,
setIsPrivacyOn
};
}
This is my custom hook. I set the state in PrivacyIcons component, and then I use isPrivacyOn for show/hide values from a table based on the value. But in a different component the isPrivacyOn is not changed, it's changed only in PrivacyIcons? Why I can't change it in one component and then use the value across all components? Thanks.
states are not meant to be shared across components. You are looking for useContext. This allows you to share a function and a state between components. React has an excellent tutorial on how to do it in the official documentation: https://reactjs.org/docs/hooks-reference.html#usecontext
For your specific example it would look something like this:
Your App.js
import { useState } from 'react';
export const PrivacyContext = createContext([]);
const App = (props) => {
const [isPrivacyOn, setIsPrivacyOn] = useState(false);
return (
<PrivacyContext.Provider value={[isPrivacyOn, setIsPrivacyOn]}>
<ComponentUsingPrivacyContext />
{props.children}
</PrivacyContext.Provider>
);
};
export default App;
Keep in mind that any component that wants access to that context must be a child of PrivacyContext
Any component that wants to use PrivacyContext:
import React, { useContext } from "react";
import {PrivacyContext} from "...your route";
const ComponentUsingPrivacyContext = (props) => {
const [isPrivacyOn, setIsPrivacyOn] = useContext(PageContext);
return (
<button onclick={setIsPrivacyOn}>
Turn Privacy On
</button>
<span>Privacy is: {isPrivacyOn}</span>
);
};
export default ComponentUsingPrivacyContext;

Triggering a function in a React parent functional component from its child

Seen similar issues here, but couldn't wrap my mind on how this works. New to functional components and React overall.
Parent contains the Child, which is a modal. Parent has a div that triggers showing the Child modal, and the Child modal has a close button that triggers its hiding. When I click on the div component in Parent, I need to show and hide the Child modal. When I click on the close button in the Child, I need to hide the Child component.
The Parent component:
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
The Child component:
import React, { useState } from "react";
const Child = (props) => {
const [buttonState, setButtonState] = useState({
buttonState: props.isOpen,
});
const onChildClick = (e) => {
e.preventDefault();
setButtonState(false);
props.onParentClick();
};
return (
<div
className={
buttonState ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={onChildClick}>Close</div>
</div>
);
};
export default Child;
For some reason, can't make this work. What am I missing here?
Looks like useState() is used incorrectly.
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
results in buttonState being { buttonState: false}, so setbuttonState(!buttonState) does not work as intended.
Here's updated Parent component with useState(false) instead (setting initial buttonState value to false)
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState(false);
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
P.S.
As #Will suggested, there is no need to create another state in Child, it can be passed from Parent
import React, { useState } from "react";
const Child = (props) => {
return (
<div
className={
props.isOpen ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={props.onParentClick}>Close</div>
</div>
);
};
It looks like onParentClick is defined so as to take an event object as a parameter and call preventDefault() on that, but you're calling it without any arguments. Does it work like this: props.onParentClick(e);?

Using Context API with useState in React.js, any downsides?

I create a context and a provider as below. As you can see, I use useState() within my provider (for state) along with functions (all passed within an object as the value prop, allows for easy destructuring whatever I need in child components).
import React, { useState, createContext } from "react";
const CountContext = createContext(null);
export const CountProvider = ({ children }) => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
const decrementCount = () => {
setCount(count - 1);
};
return (
<CountContext.Provider value={{ count, incrementCount, decrementCount }}>
{children}
</CountContext.Provider>
);
};
export default CountContext;
I wrap my app within such a provider(s) at a higher location such as at index.js.
And consume the state using useContext() as below.
import React, { useContext } from "react";
import CountContext from "../contexts/CountContext";
import Incrementer from "./Incrementer";
import Decrementer from "./Decrementer";
const Counter = () => {
const { count } = useContext(CountContext);
return (
<div className="counter">
<div className="count">{count}</div>
<div className="controls">
<Decrementer />
<Incrementer />
</div>
</div>
);
};
export default Counter;
Everything is working just fine, and I find it easier to maintain things this way as compared to some of the other methods of (shared) state management.
CodeSandbox: https://codesandbox.io/s/react-usecontext-simplified-consumption-hhfz6
I am wondering if there is a fault or flaw here that I haven't noticed yet?
One of the key differences with other state management tools like Redux is performance.
Any child that uses a Context needs to be nested inside the ContextProvider component. Every time the ContextProvider state changes it will render, and all its (non-memoized) children will render too.
In contrast, when using Redux we connect each Component to the store, so each component will render only if the part of the state it is connect to changes.

Categories