React: get a list of a child's components - reactjs

I can get a list of a component's children and the props on each child:
React.Children.map(children, child => {
if (React.isValidElement(child)) {
console.log(child.props);
}
}
What if I wanted to grab the props off a component used within the child? So for example:
<Table>
<CustomColumnA />
<CustomColumnB />
</Table>
const CustomColumnA = () => {
return (
<Column
prop1={true}
prop2={"hello"} />
);
}
From within Table, I want to grab prop1 and prop2. Is this possible? the Children.map code above will print empty objects, because CustomColumnA is being called with no props. But the component it wraps has props and I need access to that.
Thanks

Related

Component Re-rendering issue

I am working on the project in React Typescript.
I have created hierarchy of components as per requirement.
In one scenario I have to pass data from child component to parent component and I am passing function as props and it works.
Issue :
When passing data to parent component child component gets re-render it looks like. Mean to say Dropdown selection is get reset and tree control expanded nodes get collapsed and set to the position as first time rendered.
I have used useState,useEffects hooks.
I have also tried React.memo as a part of my search on internet.
What I need :
I want to pass data to parent component from child without re-render the child component as there is no change in the props of child component.
Try this approach:
Add useCallback hook to memoize your function which lift data to <Parent />.
Then use React.memo for <Child /> to control prop changes and avoid unwanted re-renders.
I prepare an example for you here.
UPD. I have uploaded an example, you can copy it and see how it works!
Here is Child component:
const Child = ({ onChange }) => {
console.log("Child re-render");
return (
<div className="App">
<h1>Child</h1>
<button onClick={() => onChange(Math.random())}>
Lift value to Parant
</button>
</div>
);
};
const areEqual = ({ onChange: prevOnChange }, { onChange }) => {
return prevOnChange === onChange; // if true => this will avoid render
}
export default React.memo(Child, areEqual);
And the Parent:
consn App = () => {
const [value, setValue] = useState("");
const onChange = useCallback((value) => setValue(String(value)), []);
console.log("Parant re-render");
return (
<div className="App">
<h1>Parent</h1>
<div>Value is: {value}</div>
<Child onChange={onChange} />
</div>
);
}
Best regards ๐Ÿš€

Pass state(index) to several children

After learning how to pass a state to a child, I am now wondering how to do it between children.
Parent:
const PostTemplate = ({ data }) => {
const [isIndex, setIndex] = useState(0);
return (
<>
<Slider
setIndex={isIndex}
{...data}
/>
<Views
setIndex={setIndex}
{...data}
/>
</>
);
};
Child1 (Views):
const Views = (data) => {
return (
<div>
{data.views.edges.map(({ node: view }, index) => (
<div
onClick={() => {
data.setIndex(index);
}}
>
<p>Hello</p>
/>
</div>
))}
</div>
);
};
Child2 (Slider):
const Slider = (data) => {
return (
<Swiper initialSlide={data.isIndex}>
{data.views.edges.map(({ node: view }) => (
<SwiperSlide>Slide</SwiperSlide>
))}
</Swiper>
);
};
This returns a rather strange error: undefined is not an object (evaluating 'el.classList').
What I would like to do is pass the index of Views to Slider.
Thanks for all the help!
Props:
Props are data which is passed to the component when it is added to ReactDOM
Props are immutable- means component can never change it's own props.
Data Flow:
When two child component have to share/use same data, parent component will pass this data to child component. Data (as Props) flows from Up to Down
Now this data is owned by Parent component. So any change of this data have to handle by this parent component also. For example, if child component wants to change this Data, they have to call parent's component change handler. Change Event flow from child to parent.
In your example PostTemplate is parent and Views & Slider are child.
PostTemplate own and manage index data (state).
PostTemplate will send index data (state) to child components: Views & Slider
Now both components have index value in their Props.
Child component Views need to change the value of Index. So parent component also pass it's own index change handler to Views component
Views component calls the change handler it got from it's parent as props when it needs to change Index value.
Here is a working example from your code in question:
function Slider(props) {
return (
<fieldset>
<legend>Slider Component </legend>
<p>Got Index data as Props: {props.index}</p>
</fieldset>);
}
class PostTemplate extends React.Component {
constructor(props) {
super(props);
this.setIndex = this.setIndex.bind(this);
this.state = {index: 0};
}
setIndex(e) {
this.setState({index: e.target.value});
}
render() {
const index = this.state.index;
return (
<fieldset>
<legend>PostTemplate Component:</legend>
<ol>
<li key={1}> This is parent component which has two child componets: Slider, Views </li>
<li key={2}> As Index data is used by both of it's child components, Index data is initilized and managed by this component.</li>
<li key={3}> When a child component needs to use this data (state:index), Posttemplate (parent) will pass the value as props </li>
<li key={3}> When a child component needs to change this data (state:index), Posttemplate (parent) will pass the changeHandler as props </li>
</ol>
<Views
index={index}
setIndex={this.setIndex}/>
<Slider
index={index} />
</fieldset>
);
}
}
function Views(props) {
return (<fieldset>
<legend>Views Component </legend>
<p>Got Index data as Props: {props.index}</p>
<p>Got index change handler function from Props: {typeof props.setIndex }</p>
<input
value={props.index}
onChange={props.setIndex} />
</fieldset>);
}
ReactDOM.render(
<PostTemplate />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js">
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js">
<div id="root">
<!-- This div's content will be managed by React. -->
</div>
Try it on CodePen

How to get the ref of a child component inside parent component in ReactJS

I have a parent component and a child component as following.
Parent component:
constructor(){
this.refs = React.createRef();
}
setRef(ref) {
console.log(ref)
}
handleChange(e) {
console.log(e)
}
render() {
return(
<ChildComponent ref={this.setRef} handleChange={this.handleChange.bind(this)}/>
)
}
Child Component:
render() {
return(
<input type="text" ref={this.props.ref} onChange={this.props.handleChange.bind(this)} > </input>
)
}
What changes should I do to get the ref value inside the handleChange() function in parent component? Thanks in advance.
If Iยดm reading correctly you want to access the input element from the parent component?
You have to use another name for your prop as ref is a keyword prop which will automatically assign the component the given variable.
<ChildComponent inputRef={this.setRef} handleChange={this.handleChange.bind(this)}/>
class ChildComponent extends Component {
render() {
return(
<input type="text" ref={this.props.inputRef} onChange= {this.props.handleChange.bind(this)} > </input>
);
}
}
Based on how you want to access the ref you can either set this.refs directly to the prop or set it inside your setRef function.
// either `this.refs` and then usable through `this.refs.current`
<ChildComponent inputRef={this.refs} {...} />
// or `this.setRef` and assign it yourself
setRef = (ref) => {this.refs = ref;}
ref(as well as key btw) is very special prop. It is not accessible in child by this.props.ref.
Shortest way is to use different prop to pass ref forward and backward:
class Parent ...
render() {
...
<Child inputRef={this.inputRef} />
class Child
...
render() {
<input ref={this.props.inputRef} ...
It's most flexible since you may access different ref inside you child component(e.g. inputRef + scrollableContainerRef + popupRef)
But in some cases you want to compose new component for existing code base. Say <input>'s replacement. Sure, in this case you would avoid changing all <input ref={...} /> onto <MyInput refProp={...}>.
Here you may use React.forwardRef. It feels the best with functional components like
export Child = React.forwardRef((props, forwardedRef) => {
...
return ...
<input ref={forwardedRef} />
})
But for class-based component you would rather use ref-prop with different name:
class Child
...
render()
return
...
<input ref={this.props.forwardedRefName} />
export ChildWithForwardRef = React.forwardRef((props, ref) => <Child forwardedRefName={ref} {...props} />)
PS since you will consume what forwardRef returns rather initial component(Child) you may want to specify displayName for it. This way you may be able find it later say with enzyme's find() or easy recognize element in browser's React DevTools
ref work only on native dom element, to make ref work on user defined Component, you need this:
https://reactjs.org/docs/forwarding-refs.html

Passing state from a child to parent component

Is there any proper way to access a property in the state of a child component and get its value from a parent component?
I have a component called "itemSelection" where I map through an api response to get some items like this
<div className="row">
{this.state.items.map(i => <Item ref="item" id={i.id} name={i.name} quantity={i.quantity} />)}
</div>
In the Item component there a property in the state called "selected" which I want to know its value if it was true or false in the itemSelection component. I know I can pass props from itemSelection to Item but what if I want the opposite? where I can pass data from Item to itemSelection
EDITED
So, I have made a property in the parent component "itemSelection" called "selected" and I have set it to =false= (knowing that I have the same property in the child component which is set to =false= also)
in the child component I have put this line in the event handler function after I have made setState to the property selected to change it to =true=
this.props.getPropsFromChild(this.state.selected);
then in the parent component I have made this function
getPropsFromChild = (selected) => {
this.setState({selected: selected});
}
but still didn't work, I don't know if I have set it right.
Passing props from child to parent component works using callback functions in React. Or you can also use state management library like Redux and store the data in child component and get the data in parent component.
The example below illustrate how to send props from child to parent. I am sharing below example to make you understand how you can send props from child to parent.
ItemSelection: Parent component
//handler function
getPropsFromChild = (id, name) => {
console.log(id, name);
}
//pass down your handler function to child component as a prop
<div className="row">
{this.state.items.map(i => <Item ref="item" id={i.id} name={i.name} getPropsFromChild={this.getPropsFromChild} quantity={i.quantity} />)}
</div>
Item: Child component
componentDidMount(){
//access handler function passed to your item component and access it using this.props and send the values as you want to the function
this.props.getPropsFromChild(โ€œ01โ€, โ€œHiโ€);
}
As tried to explain in the comments you can use callbacks for this, but try to avoid to get a value from child component like that. You can keep selected state in your parent component. Your Item component does not need to keep a state at all for this. With proper handlers from the parent, you can update your state easily.
class App extends React.Component {
state = {
items: [
{ id: "1", name: "foo", quantity: 1 },
{ id: "2", name: "bar", quantity: 2 },
{ id: "3", name: "baz", quantity: 3 },
],
selected: "",
}
handleSelect = item => this.setState({ selected: item.id })
render() {
const { items } = this.state;
return (
<div>
Selected item: {this.state.selected}
{
items.map( item =>
<Item key={item.id} item={item} onSelect={this.handleSelect} />
)
}
</div>
);
}
}
const Item = props => {
const { item, onSelect } = props;
const handleSelect = () => onSelect( item );
return (
<div style={{border: "1px solid gray"}} onClick={handleSelect}>
<p><strong>Item id:</strong> {item.id}</p>
<p><strong>Item name</strong>: {item.name}</p>
<p><strong>Item quantity</strong>: {item.quantity}</p>
</div>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Pass JSX to child, and have the child edit JSX props?

How can <MyList> add prop myListProp={true} to <SomeComponent1>?
I want MyList to manage props, and not the class where this JSX is declared (theCurrentClass)
<div className='theCurrentClass'>
<MyList>
<SomeComponent1 />
<OtherComponent2 />
<WowComponent3 />
</MyList>
</div>
Is cloneElement the only way?
I need to constantly update these props / styles, and don't want to continuously clone the JSX.
This does seem to be accounted for, both with cloneElement and JSX.
https://facebook.github.io/react/docs/react-api.html#cloneelement
return this.props.children.map((child, index) => {
return React.cloneElement(child, {
...child.props,
});
});
return this.props.children.map((child, index) => {
return ( <child.type {...child.props}></child.type> );
});
I can interface with the element by adding props the same way I do with any rendered element
NOTE: requestAnimationFrame loops should only edit the ref (passed back from the child in a callback on didMount), and rarely update props unless you want to deal with all the React "smart" analysis bogging down the 60fps

Resources