React render specific children - reactjs

I have looked through other peoples questions relating to this but cant find a suitable answer. I would like to pass children to a component and then pull out the specific children where I want them, most examples I have seen just have the children render in the same place.
My component looks something like this -
<ParentComponent>
<ChildOne/>
<ChildTwo/>
<ParentComponent/>
When I log the props.children inside the parent component I get an array which contains both children as objects. is there a simple way to pull out the specific child where I need it such as {props.children.ChildOne} at the moment I am using props.children[0] which isn't ideal as we will be passing the children dynamically
in the future and the array length may change.
As always any help is greatly appreciated!

Depending on your exact situation and needs, it might make more sense to pass child components as props than using the special children prop. Then you can render them whichever way you like.
<ParentComponent childOne={ChildOne} childTwo={ChildTwo} />
...
const ParentComponent = ({ childOne, childTwo }) => {
return (
<div>
{childOne}
<div>
{childTwo}
</div>
</div>
);
};
But knowing your exact scenario would help a lot with conceptualising the best way to implement this. Perhaps you can refactor your code to avoid passing an array of children like this.

Actually, the ReactChildren API I was mentioning is useless here.
You can do something like this instead:
import React from 'react';
import { ChildOne } from './YourFile';
export function ParentComponent({children}) {
return children.find(child => child.type === ChildOne)
}

You should define the displayName property for the child components and then use the displayName in the parent to find the specific children from children list and place them where you want it to be.
// define displayName for each component, it can be any string
// You can set the displayName for any react component before exporting as shown
// below
const ChildOne = (props) => { return (<div> Child One </div>)}
ChildOne.displayName = "ChildOne";
export default ChildOne;
const ChildTwo = (props) => { return (<div> Child Two </div>)}
ChildTwo.displayName = "ChildTwo";
export default ChildTwo;
Now in parent component you can filter out the specific child by using their displayName.
const ParentComponent = (props) => {
const getChildByDisplayName = (displayName) => {
const child = React.Children.map(props.children, (child => {
// you can access displayName property by child.type.displayName
if (child.type.displayName === displayName) return child;
return null;
}))
return child;
}
return (
<div>
{/* You can change the order here as per your wish*/}
{getChildByDisplayName("ChildOne")}
{getChildByDisplayName("ChildTwo")}
</div>
)
}
That's it, Now even if you put ChildTwo before ChildOne like below example, parent component will still render the ChildOne first and then ChildTwo because we have defined order in parent.
<ParentComponent>
<ChildTwo/>
<ChildOne/>
<ParentComponent/>

Using the key seems simpler:
whatever is using the parent component:
<ParentComponent>
<ChildOne key="title"/>
<ChildTwo key="picture"/>
<ParentComponent/>
parent component:
export default function ParentComponent(props: any) {
const title = props.children.find((o: any) => o.key === 'title')
const picture = props.children.find((o: any) => o.key === 'picture')
return <div>
<jumbobox>
{title}
</jumbobox>
<fancyframe>
{picture}
</fancyframe>
</div>
}

One way is to control the Child Components being passed to ParentComponent through state/props/redux store management in the component containing ParentComponent and its children.
But to have the solution as per your use case, we can avoid using children prop and use our defined prop on ParentComponent.
<ParentComponent
components={[
{key: 1, component: <div>Hello</div>}
]}
/>
So, we can now filter from the key.
Checkout this demo

Related

handling props in a deconstructed child

I have a React component that clones its children with additional props. I'm using the standard childrenWithProps method that works great if your child is another react component but no clear way of doing this without a direct react component as the child.
<DataCmp>
<Fragment>
<h1>Example Code</h1>
<div>{isLoggedIn}</div>
</Fragment>
</DataCmp>
In this example, I have adding the prop myData to the props of its children. However, this doesn't work. The child doesn't see the value. It will say myData is not set when it's passed in by props.
So I attempted this:
<DataCmp>
<Fragment>
<h1>Example Code</h1>
<div>{this.props.isLoggedIn}</div>
</Fragment>
</DataCmp>
This brings up errors as it has no idea what this.props.myData is.
My next attempt was to wrap the child in an inline function and get the prop from that.
<DataCmp>
{({ isLoggedIn}) => (
<Fragment>
<h1>Example Code</h1>
<div>{isLoggedIn}</div>
</Fragment>
)}
</DataCmp>
While this doesn't bring up any errors; The child component is never rendered.
I'm working on updating and modernizing someone else old Github project. here is the link to my project and the wrapping component is Wallet.jsx the location that it's being used is index.jsx
The children are rendered as such:
renderChildren = () => {
const { children } = this.props;
const { accounts } = this.state;
const handleLogin = () => this.login();
const childrenWithProps = React.Children.map(children, (child, index) => {
if(typeof child == 'object') {
return React.cloneElement(child, {
key: index,
loginFn: () => handleLogin(),
isLoggedIn: accounts[0] !== '0x0',
accounts,
});
} else {
return child;
}
});
return childrenWithProps;
}
I guess the error may not be in the destructuring, but in how you are using childrenWithProps.
It would be useful if you shared a condesandbox representing the problem with dummy data, so we can take a look there at that part too.
React.Children.map(children, fn) only iterates over valid react elements
This excludes, for example, functions passed as child. Passing a function-to-be-rendered as prop to a component is known as Render Props pattern. React.Children.map will not iterate over this, hence, your third option returned null.
Fix it by checking whether children is a valid ReactElement first and render it accordingly:
// wallet.tsx
...
const additionalProps = { ... };
if (React.isValidElement(children)) {
return React.Children.map(children,
(child, i) => React.cloneElement(child, { key: i, ...additionalProps });
} else {
// handle null, strings, undefined, booleans etc
return typeof children === 'function' ? children(additionalProps) : children;
}
...
// index.tsx
<Wallet ...>
{/* either a function */}
{(additionalProps) => console.log(additionalProps)}
{/* or components */}
<Layout>
...
</Layout>
</Wallet>
Note that React.isValidElement() also returns true for HTML-Elements. Which will receive the props but you obviously cannot add custom-logic. But let's say you pass a style props, it will be applied.

how to access data from child tags in react

I'm trying to make sure my custom parent components can have access to any children inside of them. I am trying to accomplish a setup similar to:
function App() {
return (
<div className="App">
<Parent>
<img src={spaceGasImg} height='100px' width='100px'></img>
</Parent>
</div>
);
}
I'd like to be able to get the data from my img tag into my parent and potentially even be able to remove the img tag from the parent on a hook. I'm not sure if this is best handled by using higher order components?
Is there a way to setup my parent component such that it assumes anything wrapped inside of it is a child and can read all the data of that child?
any advice on laying out how this relationship should work is greatly appreciated :)
In React you can either:
1 - Pass variables as props down to let your child components know this value declared above.
2 - Pass methods as props down that set a state value on the parent component to pass a child value above
These are the two basic ways to communicate components. There is no way you can share a child value with parent without declaring a state on the parent and providing the child the method to update it.
If you do not want to pass values all the way down, but even though you want all the children to be aware of something the best approach IMHO is using the Context API and Hooks. In your example it would be like:
const ImgContext = React.createContext()
function App() {
return (
<div className="App">
<ImgContext.Provider value={spaceGasImg}> // the name of this prop MUST be 'value'
<Parent>
<Child />
</Parent>
</ImgContext.Provider>
</div>
);
}
function Child() {
// Every component inside the Provider will have the value available in the
// useContext hook with the context as a parameter.
const contextValue = useContext(ImgContext) // spaceGasImg
return (
<img src={contextValue} />
)
}
Every child component of <ImgContext.Provider /> that makes use of useContext has access to the value prop passed on the provider
I will recommend you to take a look at React.context (https://reactjs.org/docs/context.html), you will learn a lot.
Here is how I think you will need to process:
Create a custom Parent component, with a context attached to it, then create function that will be able to generate your list of img tags, and function to alter them (removeImage(), updateImage(), getImage(), ..).
Create your own Context:
const MyContext = React.createContext(defaultValue);
Create your Parent state:
this.state = {
images: [
{
id: 1
url:'url1',
height: '400px',
width: '400px'
  },
{
id: 2
url:'url2',
height: '400px',
width: '400px'
  }
]
}
}
Create your Parent Component:
class Parent extends React.Component {
constructor(props) {
super(props);
this.removeImage = (id) => {
// write the function to remove image from this.state.images array using id
}
this.getImage = (id) => {
// write the function to get image from this.state.images array using id
}
this.state = {/* past step 2 here */}
}
render() {
return (
<div>
<MyContext.Provider value={this.state}>
<MyContext.Consumer>
{({images}) => (
<div className="list-image">
{images.map((img) => {
return (<img
imgId={img.id}
src={img.url}
height={img.height}
width={img.width} alt="nothing"
/>);
}
}
</div>
}}
</MyContext.Consumer>
</MyContext.Provider>
</div>
);
}
Like that you will be able to alter the this.state.imgaes list that will directly alter your render of images list.
Hope it's what you asked for ;)

React.js: how to set an active flag in one of multiple identical children?

I have a parent component with multiple identical children of which only one can be active at a time. The active state is to be set through an internal event on the child itself e.g. a button click, and not by the parent. I want the active state to be unset by a call from a sibling but I cant find a way for siblings to call eachother's methods. Ive tried refs but they are only accessible from the parent and i cant find a way to make a child ref available within itself without maintaining a list of refs on the parent which i dont want as i only need to store the currently active one.
Simple example
e.g.
<Parent>
<Child active={false}/>
<Child active={false}/>
<Child active={true}/>
</Parent>
where a child is something like
export class Child extends React.Component {
constructor() {
super(props);
state = {
active: props.active;
}
}
setActive(active) {
setState ({active : active});
}
onclick = () => {
// get currently active other sibling?
// call setActive direct on other sibling.
// e.g. other.setActive(false);
// set current to active using the same method
this.setActive(true);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.state.active ? "active": "inactive"}
);
}
}
I've tried passing in parent setActiveRef and getActiveRef functions as props to the children to maintain a single shared ref (the active one) and use getActiveRef().current.setActive directly but i cant find a way to access the ref of a child component from within itself to send to the setActiveRef() on the parent in the first place.
any advice much appreciated. thanks
In short, this isn't how React is designed - children won't be able to modify each other's state directly. A simple analogy would be a literal parent and their children. You want the children to know when it's time to raise their hand, but they don't take directions from each other, only from Mom or Dad. What you CAN do is tell the kids how to communicate with their parents, and let their parents deal with it.
I'm sure there are better ways, but here is some code:
export class Parent extends React.Component {
constructor() {
super(props);
state = {
activeChild: "Jimmy"
}
};
// Let the parents listen for requests
handleChildRequest (name) => {
this.setState({activeChild: name});
};
render() {
<div>
<Child active={this.state.activeChild === "Jimmy"} name="Jimmy" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Sally"} name="Sally" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Fred"} name="Fred" handleRequest={this.handleChildRequest} />
</div>
};
}
export class Child extends React.Component {
constructor() {
super(props);
// Lets forget about local state, we don't need it!
}
onclick = () => {
this.props.handleRequest(this.props.name);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.props.active ? "active": "inactive"}
);
}
}
Answer to my own question using component passing (this) references to parent callback. This is not complete code but i illustrates the point. Appears to work fine for my use case (updating realtime map locations) which is more complex than this simplified example.
parent component passes callbacks to children to store ref to active component
export class Parent extends React.Component {
activeChild = undefined;
setActiveChild = (child) => {
activeChild = child;
}
getActiveChild = () => {
return activeChild;
}
// set up some callback props on each child
render() {
return (
<Parent>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={true} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
</Parent>
)
}
each child simply calls back on the parent using prop callbacks and passes itself. this allows the state to be set internally within the component forcing a re-render if values change.
export class Child extends React.Component {
onclick = () => {
// get currently active other sibling stored in parent
let active = this.props.getActiveChild();
// call setActive directly on other sibling.
active.setActive(false);
// store currently active child in parent
this.props.setActiveChild(this);
// set 'this' component to active using the same method
this.setActive(true);
}}
criticisms and improvements most welcome.
thanks
I was looking for a way to do something similar with hooks, and this is what worked for me:
import React, { useState } from 'react';
const Parent = () => {
const [activeChild, setActiveChild] = useState(undefined);
return (
// Now the children have access to the current active child
// and also the ability to change that
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'1'}/>
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'2'}/>
)
export default Parent
then inside the Child component...
import React, { useState, useEffect } from 'react';
const Child = ({activeChild, setActiveChild, id}) => {
const [activated, setActivated] = useState(false);
useEffect(() => {
if (activeChild !== id) {
setActivated(false);
}
}, [activeChild, chapter]);
return (
//on click the component will be able to be activated and set itself as the activated component.
<div onClick={()=> {
setActivated(true);
setActiveChild(id)
}} />
)
export default Child
The useEffect in the Child will check if its id (which is unique to itself) is the id of the activeChild. If not, it'll make sure that its local state of activated is false.
Once activated though, it'll set its local state of activated to true, and set the activeChild's id to its own id.
any feedback is welcome!! This made sense in my head.
Let's supposed you have an array of childs components, each one of those child, will have a prop called active, so you could use an state variable to store the array of childs, so when one of the childs gets updated, and cause a rerender of each one of the child components as well.
import React from "react";
const Parent = () => {
const childs = [{ active: false }, { active: false }, { active: false }];
const [childrens, setChildrens] = React.useState(childs);
const onOptionSelected = idx => {
setChildrens(prevOptions =>
prevOptions.map((opt, id) => {
opt.active = id === idx;
return opt;
})
);
};
return (
<div>
{childrens.map((child, id) => {
return (
<Child
key={id}
id={id}
active={child.active}
onOptionSelected={onOptionSelected}
/>
);
})}
</div>
);
};
const Child = ({ id, active, onOptionSelected }) => {
console.log(id)
const onClick = () => {
onOptionSelected(id);
};
return (
<>
<button onClick={onClick}>set active</button>
<p>current state={active ? "active" : "inactive"}</p>
</>
);
};
export default Parent;

How does this component render its children?

I am studying the code for react-accessible-accordion, and I don't understand how it renders its children.
From Accordion.tsx:
export default class Accordion extends React.Component<AccordionProps> {
// ... defaults
renderAccordion = (accordionContext: AccordionContext): JSX.Element => {
const {
preExpanded,
allowMultipleExpanded,
allowZeroExpanded,
onChange,
...rest
} = this.props;
return <div data-accordion-component="Accordion" {...rest} />;
};
render(): JSX.Element {
return (
<Provider
preExpanded={this.props.preExpanded}
allowMultipleExpanded={this.props.allowMultipleExpanded}
allowZeroExpanded={this.props.allowZeroExpanded}
onChange={this.props.onChange}
>
<Consumer>{this.renderAccordion}</Consumer>
</Provider>
);
}
}
This component accepts a few levels of nested children. Specifically, I don't understand how they are being passed down.
I can see that the component spreads the rest of props over a self-closing Accordion div element... How does that mechanism manage to render multiple levels of children?
A React context Consumer expects a function as its child to render the content. That function in this example is referenced as this.renderAccordion:
<Consumer>{this.renderAccordion}</Consumer>
Which renders the children in the {...rest} spread attributes:
const {
preExpanded,
allowMultipleExpanded,
allowZeroExpanded,
onChange,
...rest
} = this.props;
return <div data-accordion-component="Accordion" {...rest} />;
The ...rest includes children from this.props (and you can actually render children as an attribute, like <div children={ <p>Hello!</p> } />) from the destructuring assignment -- in other words const { ...rest } = this.props includes this.props.children.
There is Provider for providing and Consumer for rendering the child components because the props are spread to the Consumer and children is a prop of Accordian.
Here is the consumer being used
For individual components such as the AccordianItem, this uses Provider to define components which are meant to be rendered.
This may help you to understand.
Basically, when JSX is compiled to React code, it creates a component using:
React.createElement("div", null, children);, or
React.createElement("div", { children }, null);
Check how Hello, Foo and Bar component are compiled in the link that I sent you. Your case is gonna be the Bar component

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