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.
Related
I understand that the normal way is to pass props through a parent, but I want to know how else I can pass props to a component.
let C1 = createReactClass({
...
render: function() {
console.log('render C1');
return (
<div>
<button onClick={this.cambiaAAzul}>Azul</button>
<button onClick={this.cambiaAVerde}>Verde</button>
<button onClick={this.cambiaARojo}>Rojo</button>
<p>Estado C2 <strong style={ {color: this.state.color} }>{this.state.color}</strong></p>
<C2 color={this.state.color}/> // It is common
</div>
);
}
});
Typically, to solve the props-drilling problem, which is what seems to be the issue you are trying to solve, the useContext hook should help.
So set up a context,
const C1ContextProvider = ({children}) => {
//...
// the Provider gives access to the context to its children
return (
< C1Context.Provider value={someValue}>
{children}
</C1Context.Provider >
);
}
... and then in your component where you need someValue, you can just do this:
import React, { useContext } from "react";
import { C1Context } from "../C1Context";
//...
const values = useContext(AirDCPPSocketContext);
cons foo = values.someValue; // <-- someValue obtained from the context
//..
I don't know how it is possible to not have parents but you can pass informations by local storage and context hooks.
Props are by definition arguments passed into a React component. Since React components are instantiated in the context of their parent, I'm not sure if it would be possible to pass props in any other way.
There are many other ways to get data in React though. You could query data from an API or other source, or you could pull from a state manager (such as redux). It just depends on what you are trying to accomplish.
Props are simply things passed into a component when it's called.
For instance:
C1.js
function C1((prop1, prop2) {
<h1>{prop1} {prop2}</h1>
})
App.js
<C1 prop1="Hello" prop2="World" />
<C1 prop1="React's" prop2="Great" />
By referencing the C1 component in App.js, you can call it with different props to produce different results.
Now I'm not too familiar with the createReactClass and render: you're using, as this is a style of React I've never learned. However, I'd guess that props provide the same function.
Your question asks about props without a parent. Well. Props without a parent wouldn't be props, they'd just be normal JS data types inside your file.
Hope this helps :)
The component's local state only can drill into child components. for passing props without using React component's local state. you have to use state management libraries like Context API or Redux
These libraries are created to make you able to pass props into nonrelated or sibling components
Example with redux:
Here I Assume that you set up redux in your project. imagine you have two components that you want to pass props without parent-child relation.
const changeMyWeightAction = (weight) => {
return {
type: 'CHANGE_MY_WEIGHT',
payload: weight
}
}
const JenniferFunctionComponent = () => {
const dispatch = useDispatch()
return (
<div>
<span>hello I'm Jennifer and I can decide my weight</span>
<input onChange={e => dispatch(changeMyWeightAction(e.target.value)})/>
</div>
)
}
const WeightFunctionComponent = () => {
const weight = useSelector(state => state.Jenny.weight)
return (
<span>Jenny's weight is {weight}</span>
)
}
I have tried pass value from parent to grandchild component, and it works. While I am thinking if there is another simpler or other way of passing props in shorter path.
What I did is quite cumbersome in codesandbox
There may be a common problem in react world called prop drilling by passing data to children only using props.
I would recommend only 2-level passing, if you need pass data deeper then you probably doing something wrong.
Use one of popular state management library (if your project is big) or React context (which is awesome)
Create a folder called /contexts and put contexts there. The structure of files can be like shown below:
First you need to create a context itself
type ClientContextState = {
data: User;
set: (data: User) => void;
logout: () => void;
};
// empty object as a default value
export const UserContext = createContext<UserContextState>({} as UserContextState);
Then create a Provider wrapper component
export const UserProvider = ({ children }: Props) => {
const [data, setData] = useState<User>({});
const sharedState = {
data,
set: setData
logout: () => setData(null)
}
return <UserContext.Provider value={sharedState}>{children}</UserContext.Provider>
});
You may also want to have an alias of useContext hook:
export const useUser = () => {
return useContext(UserContext);
};
After all this whenever you wrap your components or app to <UserProvider>...</UserProvider> you can use our hook to access data and methods form sharedState from any place you want:
export LogoutButton = () => {
const {data, logout} = useUser();
return <Button onClick={() => logout()}>Logout from {data.name}</Button>
}
Whenever you want to pass props or data from Grandparent to child component, always use react-redux. This is useful to maintain the state and access the data from anywhere/any component.
Another way is to use useContext hooks which you can use to pass the props
Following are the steps to use useContext hooks
Creating the context
The built-in factory function createContext(default) creates a context instance:
import { createContext } from 'react';
const Context = createContext('Default Value');
The factory function accepts one optional argument: the default value.
Providing the context
Context.Provider component available on the context instance is used to provide the context to its child components, no matter how deep they are.
To set the value of context use the value prop available on the
<Context.Provider value={value} />:
function Main() {
const value = 'My Context Value';
return (
<Context.Provider value={value}>
<MyComponent />
</Context.Provider>
);
}
Again, what’s important here is that all the components that’d like later to consume the context have to be wrapped inside the provider component.
If you want to change the context value, simply update the value prop.
Consuming the context: Consuming the context can be performed in 2 ways.
The first way, the one I recommend, is to use the useContext(Context) React hook:
import { useContext } from 'react';
function MyComponent() {
const value = useContext(Context);
return <span>{value}</span>;
}
Generally it's helpful to consider whether moving state down the hierarchy would be the simplest route. That means lifting the component instantiation to a place closer to the state being used. In your example, that could mean Component_data is used inside Component and passed to its children there, removing one step in the nested data flow. Even better, would be that Child.a accesses Component_data.A directly.
In a real app with cases where accessing the data directly is less feasible, a solution I lean towards is using Context to set data in the parent that retrieves it, and then I can access it however deeply nested the component might be that needs it.
i.e. in App I would create the Context provider, and in ChildA I access it via useContext hook.
Further reading
https://reactjs.org/docs/context.html
https://overreacted.io/before-you-memo/#solution-1-move-state-down (this post is about an alternative to using useMemo but has an illustrative example of why moving state down is a good thing)
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>;
}
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 />
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.