React forwardRef: ref and other props - javascript

A parent component:
const Parent = (props) => {
const ref = useRef();
return <Child ref={ref} />
}
and the child:
const Child = forwardRef((props, ref) => {
return <button ref={ref} ...>click</button>
})
What if I want to pass more props to Child than just ref?
I've searched documents and tutorials, but found nothing; and by trial-and-error, I guess this would work:
// in parent
<Child onClick={...} prop1={...} prop2={...} ref={ref} />
and then in Child, I can get these props (onClick, prop1, prop2) from props.
Is that all I need to do? By putting ref as the last prop passing to the child?
What if I have more than one button in Child that needs a ref?

// in parent
<Child onClick={...} prop1={...} prop2={...} ref={ref} />
The order is irrelevant. forwardRef extracts the ref prop from the properties and creates a wrapper.
The other props are available in props (the first argument in the forwardRef callback-function)
If you want to use multiple refs you can use the example here.

Well, i had followed this approach,
Parent component
const Parent = (props) => {
const ref = useRef();
return <Child _ref={ref} />
}
Child component
const Child = forwardRef(({ _ref, prop1, prop2, prop3, ...props }) => {
return <button ref={_ref} ...>click</button>
})
And it worked

Related

How to pass props to children according to type in React + Typescript

Apologies if this has been answered elsewhere, it seems like a basic problem but I wasn't able to find an answer anywhere.
I am trying to find a way to conditionally pass props to children components based on the the type (i.e. component type) of the child.
For instance given a generic functional component
const Parent = ({propA, propB, children, ...props}) => (
<div {...props}>
{children}
</div>
)
in which I expect to receive only A and B components as children. I want propA to be passed only to children of type A and propB to be passed only to children of type B such that
const Parent = ({propA, propB, children, ...props}) => (
<div {...props}>
{React.Children.map<React.ReactNode, React.ReactNode>(children, child => {
if (React.isValidElement(child)) {
if (/* child if of type A */)
return React.cloneElement(child, { propA })
if (/* child if of type B */)
return React.cloneElement(child, { propB })
}
})}
</div>
You can use child.type
const Parent = ({propA, propB, children, ...props}) => (
<div {...props}>
{React.Children.map<React.ReactNode, React.ReactNode>(children, child => {
if (React.isValidElement(child)) {
if (child.type===A) {
return React.cloneElement(child, { propA })
} else {
return React.cloneElement(child, { propB })
}
}
})}
</div>)
if you know the mapping between Component and its respective props, you can create an array of objects and then map over it, while passing it from the parent.
Example
//calling of parent
<Parent renderObject = {[
{prop : propA, comp: compA},
{prop : propB, comp: compB}
]}
...restOfTheProps
/>
Now you can actually code something like this using map
const Parent = ({ renderObject, ...props}) => (
<div {...props}>
{renderObject.map((renderObj)=>{
return <renderObj.comp {...renderObj.prop} />
})}
</div>
)
instead of using children, you can pass component as props.
same this you can do it!
<Modal isActive={true} >
<h1>Modal here!</h1>
</Modal>
and in Your component do same this:
type Props ={
children: React.ReactNode;
isActive: boolean;
}
const Modal:React.FC<Props> = ({children, isActive}) => {
return isActive && children;
}
export default Modal;

Get Elements, (children) of React.element

So I'm having an issue I would like to resolve, Maybe someone has an answer for it.
My problem is that I have a Component that has its own Views and Components, at the same time I have a Parent Component thats using the this specific Component.
I want to check if the child of the Child Component has some props.
Child Component
const Child = () => {
return (
<View wantedArgument={true}>
<View anotherWantedArgument={false}>
</View>
</View>
)
}
Parent Component
const Parent = () => {
return (
<Child>
</Child>
)
}
So I want to get the props values of the child views.
I can use useRef for those Views, but it's not that generic and dynamic.
My question is, is there a way I can get those elements of the child?
Thanks ahead
Eden.
You can check props of Parent's children using React.Children API.
In this example, we checking the props of every Parent's child and logging them.
If you want to go to a deeper level (Child of Child of Child etc.), do it with recursion with inductive step child.props.children (if available).
const Child = ({ prop }) => {
return <>{prop}</>;
};
const Parent = ({ children }) => {
useEffect(() => {
React.Children.forEach(children, child => {
console.log(child.props);
});
}, [children]);
return <div>{children}</div>;
};
const App = () => {
return (
<Parent>
<Child prop={1} />
</Parent>
);
};

React: Access ref passed between components via render props

I would like to access a ref passed between two external components using render props (real example). Can it be done?
function Component() {
// how to get access to `ref` here?
return (
<A>
{({ref}) => (
<B ref={ref}/>
)}
</A>
)
}
You may need React.forwardRef
Ref forwarding is a technique for automatically passing a ref through a component to one of its children.
This is typically not necessary for most components in the application. However, it can be useful for some kinds of components, especially in reusable component libraries.
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Figured it out. The ref render prop is actually badly named, it's not a ref but a function to set a ref, so we can just use a inline function (thought this may cause extra renders):
function Component() {
const bRef = useRef(null);
return (
<A>
{({ref: setRef}) => (
<B ref={ref => {
bRef.current = ref;
setRef(ref);
}}/>
)}
</A>
)
}

export Hooks in React for Nested Components?

I'm exporting hooks with nested components so that the parent can toggle state of a child. How can I make this toggle work with hooks instead of classic classes or old school functions?
Child Component
export let visible;
export let setVisible = () => {};
export const ToggleSwitch = () => {
const [visible, setVisibile] = useState(false);
return visible && (
<MyComponent />
)
}
Parent
import * as ToggleSwitch from "ToggleSwitch";
export const Parent: React.FC<props> = (props) => {
return (
<div>
<button onClick={() => ToggleSwitch.setVisible(true)} />
</div>
)
}
Error: Linter says [setVisible] is unused variable in the child... (but required in the parent)
You can move visible state to parent like this:
const Child = ({ visible }) => {
return visible && <h2>Child</h2>;
};
const Parent = () => {
const [visible, setVisible] = React.useState(false);
return (
<div>
<h1>Parent</h1>
<Child visible={visible} />
<button onClick={() => setVisible(visible => !visible)}>
Toggle
</button>
</div>
);
};
If you have many child-components you should make more complex logic in setVisible. Put object to useState where properties of that object will be all names(Ids) of child-components
as you know React is one-way data binding so if you wanna pass any props or state you have only one way to do that by passing it from parent to child component and if the logic becomes bigger you have to make it as a global state by using state management library or context API with react hooks use reducer and use effect.

Passing/Accessing props in stateless child component

I know you can pass all a react components props to it's child component like this:
const ParentComponent = () => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...this.props} />
</div>
)
But how do you then retrieve those props if the child component is stateless? I know if it is a class component you can just access them as this.prop.whatever, but what do you pass as the argument into the stateless component?
const ChildComponent = ({ *what goes here?* }) => (
<div>
<h1>Child Component</h1>
</div>
)
When you write
const ChildComponent = ({ someProp }) => (
<div>
<h1>Child Component {someProp}</h1>
</div>
)
From all the props that you are passing to the childComponent you are just destructuring to get only someProp. If the number of props that you want to use in ChildComponents are countable(few) amongst the total number of props that are available, destructuring is a good option as it provides better readability.
Suppose you want to access all the props in the child component then you need not use {} around the argument and then you can use it like props.someProp
const ChildComponent = (props) => (
<div>
<h1>Child Component {props.someProp}</h1>
</div>
)
Are you looking for the ES6 named argument syntax (which is merely destructuring) ?
const ChildComponent = ({ propName }) => (
<div>
<h1>Child Component</h1>
</div>
)
const ChildComponent = (props) => ( // without named arguments
<div>
<h1>Child Component</h1>
</div>
)
Optionally there is a second argument to your function depending of whether you specified a context for your component or not.
Perhaps it would be more helpful wityh a links to the docs. As stated in the first article about functional components. Whatever props passed on to the component is represented as an object passed as first argument to your functional component.
To go a little further, about the spread notation within jsx.
When you write in a component :
<Child prop1={value1} prop2={value2} />
What your component will receive is an plain object which looks like this :
{ prop1: value1, prop2: value2 }
(Note that it's not a Map, but an object with only strings as keys).
So when you're using the spread syntax with a JS object it is effectively a shortcut to this
const object = { key1: value1, key2: value2 }
<Component {...object}/>
Is equivalent to
<Component key1={value1} key2={value2} />
And actually compiles to
return React.createElement(Component, object); // second arg is props
And you can of course have the second syntax, but be careful of the order. The more specific syntax (prop=value) must come last : the more specific instruction comes last.
If you do :
<Component key={value} {...props} />
It compiles to
React.createElement(Component, _extends({ key: value }, props));
If you do (what you probably should)
<Component {...props} key={value} />
It compiles to
React.createElement(Component, _extends(props, { key: value }));
Where extends is *Object.assign (or a polyfill if not present).
To go further I would really recommend taking some time to observe the output of Babel with their online editor. This is very interesting to understand how jsx works, and more generally how you can implement es6 syntax with ES5 tools.
const ParentComponent = (props) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...props} />
</div>
);
const ChildComponent = ({prop1, ...rest}) =>{
<div>
<h1>Child Component with prop1={prop1}</h1>
<GrandChildComponent {...rest} />
</div>
}
const GrandChildComponent = ({prop2, prop3})=> {
<div>
<h1>Grand Child Component with prop2={prop1} and prop3={prop3}</h1>
</div>
}
You can use Spread Attributes reducing code bloat. This comes in the form of {'somearg':123, ...props} or {...this.props}, with the former allowing you to set some fields, while the latter is a complete copy. Here's an example with ParentClass.js :
import React from 'react';
import SomeComponent from '../components/SomeComponent.js';
export default class ParentClass extends React.Component {
render() {
<SomeComponent
{...this.props}
/>
}
}
If I do, <ParentClass getCallBackFunc={() => this.getCallBackFunc()} />, or if I do <ParentClass date={todaysdatevar} />, the props getCallBackFunc or date will be available to the SomeComponent class. This saves me an incredible amount of typing and/or copying/pasting.
Source: ReactJS.org: JSX In Depth, Specifying the React Element Type, Spread Attributes. Official POD:
If you already have props as an object, and you want to pass it in JSX, you can use ... as a “spread” operator to pass the whole props object. These two components are equivalent:
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}```
Now, let's apply this to your code sample!
const ParentComponent = (props) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...props} />
</div>
);
I thought I would add a simple ES2015, destructuring syntax I use to pass all props from a functional parent to a functional child component.
const ParentComponent = (props) => (
<div>
<ChildComponent {...props}/>
</div>
);
Or if I have multiple objects (props of parent, plus anything else), I want passed to the child as props:
const ParentComponent = ({...props, ...objectToBeAddedToChildAsProps}) => (
<div>
<ChildComponent {...props}/>
</div>
);
This destructuring syntax is similar to the above answers, but it is how I pass props along from functional components, and I think it is really clean. I hope it helps!
But how do you then retrieve those props if the child component is stateless?
const ChildComponent = ({ *what goes here?* }) => (
<div>
<h1>Child Component</h1>
</div>
)
ChildComponent holds the name and the props will be the argument in the arrow function syntax just as you need:
const ChildComponent = props => (
<div>
<p>{props.value ? props.value : "No value."}</p>
</div>
);
If you Babel-it it will create something like this:
var ChildComponent = function ChildComponent(props) {
return React.createElement(
"div",
null,
React.createElement(
"p",
null,
props.value ? props.value : "No value."
)
);
};
For some reason, what seems to work for me is a variation on Shubham's answer above:
const ChildComponent = props => (
<div>
<h1>Child Component {props[0].someProp}</h1>
</div>
)
Using this
const ParentComponent = ({ prop1, prop2, prop3 }) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...{ prop1, prop2, prop3 }} />
</div>
);
const ChildComponent = ({ prop1, prop2, prop3 }) =>{
<div>
<h1>Child Component with prop1={prop1}</h1>
<h1>Child Component with prop2={prop2}</h1>
<h1>Child Component with prop2={prop3}</h1>
</div>
}

Categories