React forwardRef to children prop - reactjs

I have the following component that decides whether or not a component should be rendered based upon user permissions:
const Can = forwardRef(({ requiredPermissions, children }, ref): null | JSX.Element => {
const {
user: { role },
userPermissions,
} = useAuth();
return userHasPermission(role, userPermissions, requiredPermissions) ? children : null;
});
export default Can;
What I need to do is pass the ref to the child component that will be inside Can when the component is used.
How can I achieve that?

Yes you can. But you need to do two additional things:
You must be sure that only one child is being passed to the component. You can check it with the React.Children.only method.
Use React.cloneElement to pass ref to the child.
const Can = forwardRef(({ requiredPermissions, children }, ref): null | JSX.Element => {
const {
user: { role },
userPermissions,
} = useAuth();
return userHasPermission(role, userPermissions, requiredPermissions)
? React.cloneElement(React.Children.only(children), {
ref,
})
: null;
});
export default Can;

Use React.Children and React.cloneElement to pass the ref props to all child elements of Can.
Like this:
const Can = forwardRef(
({ requiredPermissions, children }, ref): null | JSX.Element => {
const {
user: { role },
userPermissions,
} = useAuth();
const childrenWithProps = () =>
React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return (
<React.Fragment>
{React.cloneElement(child, {
ref,
})}
</React.Fragment>
);
}
return child;
}) as React.ReactElement;
return userHasPermission(role, userPermissions, requiredPermissions)
? childrenWithProps()
: null;
}
);
export default Can;

Related

Higher Order Component to observe Visibility: React?

I have created a higher order component as shown below:
import React from 'react';
interface IVisibility {
Component: JSX.Element;
visibilityThreshold?: number;
onVisibleCallback?: () => void;
}
const VisibilityHandler = ({
Component,
visibilityThreshold,
onVisibleCallback
}: IVisibility) => {
const ref = React.useRef(null);
React.useEffect(() => {
const componentObserver = new IntersectionObserver(
(entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
onVisibleCallback ? onVisibleCallback() : null;
}
},
{
rootMargin: '0px',
threshold: visibilityThreshold ?? 0
}
);
const current = ref.current;
if (current) componentObserver.observe(current);
return () => {
componentObserver.disconnect();
};
}, [visibilityThreshold, onVisibleCallback]);
return <section ref={ref}>{Component}</section>;
};
export default VisibilityHandler;
And use it like this:
<VisibilityHandler Component={<div>Hello World</div>} />
However this wraps every component into a section which I don't want. I tried using React.Fragment but that doesn't let you pass ref to track the component. Is there a better way to re-create this HOC in order to incorporate visibility tracking without wrapping it in additional div or section?
You can use
function as a children
React.cloneElement
Function as a children
<VisibilityHandler Component={({ ref }) => <div ref={ref}>Hello world</div>} />
You have to change you HOC code
- return <section ref={ref}>{Component}</section>;
+ return Component({ ref });
React.cloneElement
Documentation
your case
- return <section ref={ref}>{Component}</section>;
+ return React.cloneElement(Component, { ref });
But I highly recommend use hook (packages) instead of HOC.
react-use: useIntersection
react-intersection-observer
I found a really neat way to do so like this:
import React from 'react';
interface IVisibility {
Component: JSX.Element;
visibilityThreshold?: number;
onVisibleCallback?: () => void;
}
const VisibilityHandler = ({
Component,
visibilityThreshold,
onVisibleCallback
}: IVisibility): JSX.Element => {
const ref = React.useRef(null);
React.useEffect(() => {
const componentObserver = new IntersectionObserver(
(entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
onVisibleCallback ? onVisibleCallback() : null;
}
},
{
rootMargin: '0px',
threshold: visibilityThreshold ?? 0
}
);
const current = ref.current;
if (current) componentObserver.observe(current);
return () => {
componentObserver.disconnect();
};
}, [visibilityThreshold, onVisibleCallback]);
return <Component.type {...Component.props} ref={ref} />;
};
export default VisibilityHandler;

React Context State Not Updating

My Context:
type Props = {
children: React.ReactNode;
};
interface Context {
postIsDraft: boolean;
setPostIsDraft: Dispatch<SetStateAction<boolean>>;
}
const initialContext: Context = {
postIsDraft: false,
setPostIsDraft: (): void => {},
};
const EditPostContext = createContext<Context>(initialContext);
const EditPostContextProvider = ({ children }: Props) => {
const [postIsDraft, setPostIsDraft] = useState<boolean>(
initialContext.postIsDraft
);
return (
<EditPostContext.Provider
value={{
postIsDraft,
setPostIsDraft,
}}
>
{children}
</EditPostContext.Provider>
);
};
export { EditPostContext, EditPostContextProvider };
I set postIsDraft in the parent:
export const ParentComponent = () => {
{ setPostIsDraft } = useContext(EditPostContext);
// some code
const updatePostStatus = (postStatus: boolean) => {
setPostIsDraft(postStatus);
}
// some code
return(
<EditPostContextProvider>
<ChildComponent />
</EditPostContextProvider>
)
}
Then I need to read the value in the child component:
const { postIsDraft } = useContext(EditPostContext);
Only just starting use context, just not sure what I've done wrong. When I try and read the value from context in the child component, I'm only getting back the initial value, not the set value in the parent component.
Your ParentComponent should be wrapped inside provider so as to use it's value:
<EditPostContextProvider>
<ParentComponent />
</EditPostContextProvider>
Generally we can put the provider in index.js file, and wrap <app /> in it

How to know "children" prop component type in a functional component?

I want to assign props dynamically in the children of a component
Ex: I want to check if the component passed in children here is of type Second, and in that case, add a "prova" prop
export const First: React.FC<PropsWithChildren<{}>> = ({ children }) => {
const modifiedChildren = useMemo(
() =>
React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
if(child.type === 'Second')
return React.cloneElement(child, { ...child.props, prova: "ciao" });
}
}),
[children]
);
return <>{modifiedChildren}</>;
};
The "type" property on the child doesnt seem to return the name as it does in Class components (as from what I've seen in other people examples), how am I supposed to do this the correct way ?
// main
export const MainPage = () => {
return (
<First>
<Second></Second>
</First>
);
};
// second
export const Second: React.FC<{ prova?: string }> = ({ prova }) => {
return <div>{prova}</div>;
};
I just realizeed I can just do child.type === Second (second being the functional component/the function itself)
const Second: React.FC<{ prova?: string }> = ({ prova }) => {
return <div>{prova}</div>;
};
React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
if (child.type === Second) {
return React.cloneElement(child, { ...child.props, prova: "bruh" });
}
}
}),

How to pass a value from parent to child in ReactJS/nextjs to

I am new to reactjs/nextjs and need some help on how to pass a value from one page to another
I want to pass a value in my "Apply.jsx" page to confirmation.jsx page. The value is "name=joe"
Apply.jsx
Router.push({
pathname: "/jobseeker/confirmation" })
confirmation.jsx. (need to get value in this function)
const Confirmation = props => {
const { children, classes, view, ...rest } = props;
return (
<div className="cd-section" {...rest}>
<CardWithoutImg bold view="Seeker" link="confirm" orientation="left" />
</div>
);
};
export default withStyles(blogsStyle)(Confirmation);
You can pass it as query
const handler = () => {
Router.push({
pathname: '/jobseeker/confirmation',
query: { name: 'joe' },
})
}
And in Confirmation you can retrieve it using useRouter hook
const Confirmation = props => {
const router = useRouter();
console.log(router.query) // { name : 'joe' }
const { children, classes, view, ...rest } = props;
....
};

how can put ref to props.children

I want wrapping module for multi use.
so, I make an ItemComponent
export const DragItem = (props: DragProps) => {
const [{... }, fooRef] = useFoo({
})
return (
props.children // how can i send fooRef to here??
)
}
I should send ref to props.children
Is it possible?
check this : https://codesandbox.io/s/gracious-williams-ywv9m
You need to use React.cloneElement to attach/pass extra data to children
export const DragItem = (props: DragProps) => {
const [foo, fooRef] = React.useState({});
var childrenWithRef = React.Children.map(props.children, function(child) {
return React.cloneElement(child, { fooRef: fooRef });
});
return childrenWithRef;
};
I got it.
export const DragItem = (props: DragProps) => {
const [{... }, fooRef] = useFoo({
})
return (
React.cloneElement(props.children, { ref: fooRef })
)
}

Resources