React: Access ref passed between components via render props - javascript

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>
)
}

Related

Can't pass useState() 'set' function to grand child

I'm having issues trying to get my useState variable to work. I create the state in my grandparent then pass it into my parent. Here's a simplified version of my code:
export function Grandparent(){
return(
<div>
const [selectedID, setSelectedID] = useState("0")
<Parent setSelectedID2={setSelectedID} .../> //(elipses just mean that I'm passing other params too)
<div />
)}
Parent:
const Parent = ({setSelectedID2 ...}) => {
return(
<div>
{setSelectedID2("5")} //works
<Child setSelectedID3={setSelectedID2} />
</div>
)
}
From the parent I can use 'setSelectedID2' like a function and can change the state. However, when I try to use it in the child component below I get an error stating 'setSelectedID3' is not a function. I'm pretty new to react so I'm not sure if I'm completely missing something. Why can I use the 'set' function in parent but not child when they're getting passed the same way?
Child:
const Child = ({setSelectedID3 ...}) => {
return(
<div >
{setSelectedID3("10")} //results in error
</div>
);
};
In React you make your calculations within the components/functions (it's the js part) and then what you return from them is JSX (it's the html part).
export function Grandparent(){
const [selectedID, setSelectedID] = useState("0");
return(
<div>
<Parent setSelectedID2={setSelectedID} .../> //(elipses just mean that I'm passing other params too)
<div />
)}
You can also use (but not define!) some js variables in JSX, as long as they are "renderable" by JSX (they are not Objects - look for React console warnings).
That's your React.101 :)
Here's a working example with everything you have listed here. Props are passed and the function is called in each.
You don't need to name your props 1,2,3.., they are scoped to the function so it's fine if they are the same.
I moved useState and function calls above the return statement, because that's where that logic should go in a component. The jsx is only used for logic dealing with your display/output.
https://codesandbox.io/s/stupefied-tree-uiqw5?file=/src/App.js
Also, I created a working example with a onClick since that's what you will be doing.
https://codesandbox.io/s/compassionate-violet-dt897?file=/src/App.js
import React, { useState } from "react";
export default function App() {
return <Grandparent />;
}
const Grandparent = () => {
const [selectedID, setSelectedID] = useState("0");
return (
<div>
{selectedID}
<Parent setSelectedID={setSelectedID} selectedID={selectedID} />
</div>
);
};
const Parent = ({ selectedID, setSelectedID }) => {
setSelectedID("5");
return (
<div>
{selectedID}
<Child setSelectedID={setSelectedID} selectedID={selectedID} />
</div>
);
};
const Child = ({ selectedID, setSelectedID }) => {
setSelectedID("10");
return <div>{selectedID}</div>;
};
output
10
10
10
const [selectedID, setSelectedID] = useState("0")
should be outside return

setState in separated component

So I'm in the middle of my project and I found a problem that's too complicated for me.
I need to use sth like this:
{this.state.isOpen && <MyComponent />}
but state that I want to refer to is inside hoc, let's name it AppHOC. AppHOC works with some components that I need to display in Root by clicking on an but Icon is also separated and it looks like:
const Root = () => {
return(
{this.state.isOpen && <MyComponent />}
<Wrapper>
<Icon />
</Wrapper>
);
}
So the problem is: App onClick have to setState of AppHOC and recieve one of functions that AppHOC contains, and Root file have to get that state from AppHOC. Is it possible? Can I do it using Redux? Does Redux work with HOC like one reducer but state separated for every generated MyComponent? Should I use HOC here or not?
This is a common case for lifting the state up. A common parent should host the state, which seems to be Root in this case.
Since passing a state through props to
multiple deeply nested components can be cumbersome, it can be passed through React context:
const OpenContext = React.createContext();
const Root = () => {
let [open, setOpen] = React.useState(false);
let openState = React.useMemo(() => [open, setOpen], [open]);
return(
<OpenContext.Provider value={openState}>
{open && <MyComponent />}
<Wrapper>
<Icon />
</Wrapper>
</OpenContext.Provider>
);
}
The state can be accessed in nested components with context API:
let [open, setOpen] = React.useContext(OpenContext);
This is a problem that Redux can solve but it's not required.

React forwardRef: ref and other props

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

Add props as an HTML attribute in a React.js component

I recently started using Next.js and Tailwindcss. I want to create a custom component that can be used as follows -
<CustomComponent> Hello World </CustomComponent>
While searching for how to use this, I came across an answer which said -
const CustomComponent = props => {
return (
<>
<div {...props} className="text-red-900" />
</>
)
}
In my example, the CustomComponent will simple make the text-color to a shade of red.
I can now use this component as <CustomComponent> Hello World </CustomComponent>.
What I don't understand is this line - <div {...props} className="text-red-900" />. I couldn't find anything like this in the reactjs docs.
Is this an appropriate way of passing props as HTML attributes? How does it work?
Here are equivalent components:
const CustomComponent = (props) => {
return <div className="text-red-900" {...props} />;
};
// props={prop1,prop2,className}
const CustomComponent = ({ ...props }) => {
return <div className="text-red-900" {...props} />;
};
const CustomComponent = ({ prop1, prop2, className }) => {
// className will be overridden if its valid
return <div className="text-red-900" prop1={prop1} prop2={prop2} className={className} />;
};
Note that better to use spread syntax (for object literals) after the inline style (className) which makes it more generic.
As for why spreading the property is a valid syntax?
You have a Spread Attributes explained in the docs, remember that JSX is converted to React.createElement which accepts the props as an argument.
JSX In Depth

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.

Categories