Why pass functions as children in React component? - reactjs

I'm new to React, still struggling in understanding the design of React component.
I saw some code as below:
export const ProModeContext = React.createContext();
export class XXX extends Component {
...
render() {
return <ProModeContext.Consumer>
{ contextData => {xxx }
</ProModeContext.Consumer>
}
}
and the pattern is
<Abc>
{
param => {xxx} // let's say this anonymous arrow function is called f1
}
</Abc>
so my questions are:
Q1- is f1 a child of Abc component, therefore it can be accessed as this.props.children inside the component?
Q2-if f1 is a child of Abc component, how f1 get called inside the Abc component?
export class Abcextends Component {
...
this.props.children??? //how to invoke `f1` internally?
}
since children props is not an array, we can't invoke f1 as this.props.children[0]()

Conceptually any valid JSX is a valid JavaScript function.
JavaScript functions accept JavaScript expressions or functions as arguments, which is roughly what's happening when you do
<h1>{ person.firstName }</h1>
Here you create a function h1, that accepts the argument person.firstName, again, roughly.
As to the question why it's not all props, if everything was props then it's be something akin to using callbacks.
By utilizing the parent-children pattern we can have a more declarative UI.
Now, why would we want to do this? There are a plethora of reasons but one good reason is that in some cases it's more concise where you don't want to declare a variable for a list of JSX items.
You asked the question about this pattern below:
<Abc propName={param => xxx}>
This pattern is called a render prop - and it's great to use when you want to separate logic and rendering.
The declarative pattern and the render prop pattern serves different use cases and should be applied when necessary to the problem at hand and neither is a one size fits all solution.
Edit:
Your first example looks like this compiled from JSX to JavaScript
React.createElement(
ABC, null, counts.map(count => React.createElement("p", null, count))
)
and the API for createElement looks like this:
React.createElement(
type,
[props],
[...children]
)

If I am not misinterpreting your question, you are asking about the render prop pattern.
So render props are basically used when you want some render decisions in the child to be taken by the parent. Eg:
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)} id={someId}/>
Here assume DataProvider is a component which just fetches data from some API based on id. Now what to render after fetch is decided by the render props. So the implementation for DataProvider would just be:
Call API for someId.
Call render prop function with the fetched data.
One usecase of this pattern is separation of concern. DataProvider is just concerned with fetching data. The parent is just concerned about rendering it. There are other use cases as well described in the official docs linked above.
Passing as children :
function Parent() {
return (
<Child>
<div>Some child content</div>
</Child>
);
}
function Child({ children }) {
return <div>{children}</div>;
}
Passing with render prop:
function Parent() {
return <Child render={(text) => <div>Some child {text}</div>} />;
}
function ChildWithRenderProp({ render }) {
return <div>{render('content')}</div>;
}

Related

What is using a function within between the two tags of a React component called?

For example, in the #headless-ui/react library, the Listbox.Option can pass down the values of its internal props through the following:
<Listbox.Option>
{({ selected }) => (/* some component here */)}
</Listbox.Option>
What is this called, and how can one write code that achieves a similar function?
It's called a render prop.
You can easily make your own render props by using the children component as a function.
function MyComponent({ children }) {
// ...
return <div>{children(...)}</div>
}
<MyComponent>
{(...) => ...}
</MyComponent>
This pattern is named rather descriptively: Function-as-Child-Component. It's a variation on render-props.
Authoring a component that works in this way can be as simple as literally calling the children prop passed in (e.g. children()), though it's recommended to at least run React.Children.only(children)() to ensure that children is not an array.
React.Children.only documentation here, for reference.

Passing one Prop vs Many Props vs Nested Components in React?

This is a part of Think in React.
Thinking in React is the hard part for me because I see many developers do React with different mindsets.
When I was writing code for the Notification component that will be used by developers, suddenly I've noticed that there are different experiences to use the component:
Passing many Props like Bootstrap
<Notification
title="New Feature"
body={message}
action={action}/>
Passing one Prop as an Object
const data = {
title:"",
subtitle:"",
message:""
}
<Notification data={data}/>
Passing nested Children
<Notification>
<Title></Title>
<Body><Body/>
<Action><Action>
</Notification>
I followed the passing nested Children because ( I guess) It seems if I scale the component, I don't need to provide a Bootstrap-like experience for the developers.
import React from "react"
import { Wrapper, Text } from "./Styled"
const Body = ({ message }) => (
<Wrapper>
<Text>{message}</Text>
</Wrapper>
)
export default Body
The problem is I'm thinking about it is when I want to scale the Component and let's say adding 3 additional features that require 3 additional props
I'm confused about the reasons why each approach might be chosen, and what's the "best" developer experience.
To answer this question let's review all possibilities given React Element and a Function Component:
const c1 = <div>React Element</div>;
const C2 = () => <div>Function Component</div>;
Notice that from performance perspective, every component can be memoized and not cause useless renders.
Pass React element through props
const ObjectProps = ({ component }) => {
console.log("render object props");
return component;
};
<ObjectProps component={c1} />
Pros
Simple.
Lets you define the exact usage of passed component (contract).
For example you might decide "messages" have to be on top of "actions"
Cons
No lazy rendering
Passing heavy component may cause performance issues.
Hard to inject props (need to use React.cloneElement).
As a library writer you would like to inject your styles and refs.
Passing Function Component
const FunctionComponentProps = ({ FunctionComponent }) => {
console.log("render function component");
return <FunctionComponent />;
};
<FunctionComponentProps FunctionComponent={C2} />
Pros
Lazy rendering.
Easy to pass props and inject props for inner implementation.
Lets you define the exact usage of passed component (contract).
Cons
Confusing
Children Render
const ChildrenRender = ({ children }) => {
console.log("render function component");
return children;
};
<ChildrenRender>
{c1}
<C2 />
</ChildrenRender>
Pros
No restriction for the developer (no contract)
Cons
Hard to inject children (need to use React.Children API + React.cloneElement combo)
No contract
The developer might pass "buttons" and then "messages" and break the view.
Implementing ChildrenRender usually comes with component instances which results a minimal "contract" mentioned above.
const ChildrenRender = ({ children }) => {...};
ChildrenRender.InnerComp1 = <SomeComponent .../>
ChildrenRender.InnerComp2 = <SomeComponent2 .../>
<ChildrenRender>
<ChildrenRender.InnerComp1>{c1}</ChildrenRender.InnerComp1>
<ChildrenRender.InnerComp2><C2/></ChildrenRender.InnerComp2>
</ChildrenRender>
In Conclusion
It heavily depends on the component's usage, usually the hybrid approach suits well - passing components through props and add an option for passing children too.
Another technique is Render Props.

why Higher-Order Components is components?

I'm new to React, still struggling in understanding HOC.
We know that:
A HOC is a function that accepts a component and returns a new component that wraps around it
to provide additional features.
for example:
export function ProFeature(FeatureComponent) {
return function(props) {
if (props.pro) {
let { pro, ...childProps} = props;
return <FeatureComponent {...childProps} />
} else {
return (
<h5 className="bg-warning text-white text-center">
This is a Pro Feature
</h5>
)
}
}
}
so ProFeature is a HOC.
But to call a thing to be a component, it needs to render content directly. For example, for stateless component called customComponent, we should be able to render its content by:
<CustomComponent />
but we can't do this on HOC as:
<ProFeature /> //invalid
instead we need to do :
let Abc = ProFeature(CustomComponent);
then
<Abc/>
so HOC is not a component? if it is not a component, why it is called High Order Component? shouldn't it be called sth like High Order Function etc?
"so HOC is not a component?" - no, why should it? As React docs states:
"Concretely, a higher-order component is a function that takes a component and returns a new component."
"let abc = ProFeature(customComponent);" - it still won't work, since HOC is a curried function, you have to call it once again to return a component.
"<abc />" - still won't work since component name has to be capitalized:
const Abc = ProFeature(props)(customComponent);
<Abc />

React implicit mapping mechanism

I am going through the tutorial:
https://www.robinwieruch.de/gentle-introduction-higher-order-components/
And they have such kind of statements:
const withTodosNull = (Component) => (props) =>
!props.todos
? null
: <Component { ...props } />
As I understand Component is passed to the function, then its props get implicitly taken and fed into the return function. I do not understand how the React is doing that. I would honestly expect something like (Component) => (Component.props). What is the mechanism for this? Is it mapped correctly only if we supply the argument as props or we can supply any name? Is there a specific name for such implicit assignment?
Update
Maybe I was not clear enough, but what I am really interested in is from where props appear in the inner function if they are not passed to the previous, outer, function. I understand how the HOCs work, how to think about them, but this moment is very unclear and what in React is doing that? Is there some kind of an engine running behind the scenes, idk...
This technique is called higher-order components (HOCs) and is a way of extending components with some extra functionality.
It might look easier at first if you rewrite it using regular functions instead of arrow functions:
function withTodosNull(Component) {
return function(props) {
if (!props.todos) {
return null;
} else {
return <Component {...props} />
}
}
}
withTodosNull takes in a component and returns a new component. If this new component that is returned gets a todos prop, the component passed into the HOC will be rendered with all the props. If todos is not given as a prop, null will be rendered.
It will be probably easier to understand if we rewrite arrow functions using classic function():
function withTodosNull(Component) {
return function(props) {
if (!props.todos) {
return null;
}
return <Component {...props} />;
}
}
The inner unnamed function is a functional component. It takes properties and renders either as null or as Component.
The outer function is something called high-order-component (HoC). It is a function, that wraps a component and returns a new component.
There is no connection between Component and props. They are only parameters of two different functions.
Specifically, when you call:
class MyComponent: React.Component {
}
const myComponentWithTodosNull = withTodosNull(MyComponent);
it is the same as writing:
const myComponentWithTodosNull = props => {
if (!props.todos) {
return null;
}
return <MyComponent {...props} />;
}
Higher-Order Components are functions that "enhance" components passed as a parameter. To understand where the props are coming from let's see what would it look like to use such component.
There's our basic component, which will be passed to the HoC:
function TodoList(props) {
return (
<div>We have {props.todos.length} tasks to do!</div>
);
}
And now, we can use our HoC to create new "enhanced" component, which prevents displaying this message, when there aren't any tasks left:
const EnhancedTodoList = withTodosNull(TodoList);
Then we can use this new component, to render the message (or not, if there aren't any tasks):
<EnhancedTodoList todos={someTodos} />
As you can see, EnhancedTodoList is the first component, which gets todos. Then it decides if props should be passed to TodoList, or should it return null, when there aren't any todos.
Todos are passed explicitly from the component which renders the HoC. EnhancedTodoList acts just like a filter for TodoList.

GraphQL HOC messes with ref to children | React

I am using React and Apollo for my project.
I got this in my component MenuModal:
onClick = () => {
console.log(this.child)
this.child.onSubmit(); // do stuff
};
render() {
return (
<Modal.Content scrolling>
<MenuEdit
ref={ref => (this.child = ref)} // Using ref to access it's properties
selectedValues={selectedValues}
match={match}
menu={menu}
/>
My component MenuEdit has a function defined in class:
onSubmit = values => {
console.log('calling inner form submit', values);
if (this.child) {
this.child.submitFromOutside();
}
};
I should be able to call onSubmit from MenuModal right?
But I am currently getting this:
And when I console.log this.child in my onClick function I can see this:
So there's no onSubmit function there. When seeing GraphQL I wondered if it had something to do with me exporting the component with the graphQL HOC.
export default compose(
graphql(UPDATE_MENU, { name: 'updateMenu' }),
withApollo,
withRouter
)(MenuEdit);
And when I changed it just to:
export default MenuEdit;
I can see my function
So I wonder how I write my export so I still can access my function in my child. Thanks.
The HOC wrapps your component into another component. You can see this in the React devtools. You will see that the component renders a component around your wrapped component. Something like this
<Apollo(ChildComponent)>
<ChildComponent />
</Apollo>
Your ref then points to the Apollo(ChildComponen) element instance.
What you are doing here looks like an antipattern to me. In React we usually don't call functions on rendered elements (except sometimes DOM elements). The idea is rather that children call functions of their parents by receiving them as properties. The best thing in your case is to get rid of the reference and move your state and actions up the component chain. Alternatively you can use the new render prop style in react-apollo.
There was a contribution to the Apollo repository to address this issue...
https://github.com/apollographql/react-apollo/pull/410
Wrapping your component export like this withApollo(Component, { withRef: true }) will expose your child methods. Accessible using ref.current.wrappedInstance.

Resources