How to convert HOC to react custom hook - reactjs

I have below code snippets, just wondering apart from passing a component to withGroupInput, do we have another way to re-use this GroupedInputWithLabel with different components? Thanks
export const GroupedInputWithLabel = (props) => {
const { required, children, fieldName } = props;
const inputComponent = (
<>
<ControlLabel htmlFor={fieldName} required={required} />
{children}
</>
);
return <GroupedInput {...props}>{inputComponent}</GroupedInput>;
};
export const withGroupInput = (props, Component) => (
<GroupedInputWithLabel {...props}>
<Component {...props} />
</GroupedInputWithLabel>
);

Related

Is it ok to use react state in render prop?

I have two components App and MyComponent, where MyComponent is used in App.
import { useState } from "react";
import { MyComponent } from "./myComponent";
export const App = () => {
const [state, setState] = useState(0);
return (
<>
<MyComponent
render={() => (
<button onClick={() => setState((prev) => prev + 50)}>{state}</button>
)}
/>
</>
);
}
export const MyComponent = (props) => {
const Content = props.render;
return (
<div>
<Content/>
</div>
);
};
Is it ok to use state in the return value of the render prop? Is it considered anti-pattern?
Is it ok to use react state in render prop?
Yes, but... why? children prop was created to achieve exactly what you want here.
<MyComponent>
<button onClick={() => setState((prev) => prev + 50)}>{state}.</button>
</MyComponent>
export const MyComponent = ({ children }) => (
<div>
{children}
</div>
);

React pass multiple properties and children

I'm new to React, typescript and nextjs and I'm trying to pass multiple properties to a component. But, I seem to override them.
For example I have this in my _app.tsx
function App({ Component, myProps }: MyAppProps): JSX.Element {
const { ...props } = myProps;
return (
<MyStateProvider>
<Component {...props} />{' '}
</MyStateProvider>
);
}
In MyStateProvider, I have this in my-state-context.tsx
export const MyStateProvider: React.FC<any> = ( {children}) => {
const contextValue = doSomething();
return (
<MyStateContext.Provider value={contextValue}>
{children}
</MyStateContext.Provider>
);
};
However, I wish to something like this in my _app.tsx
function App({ Component, myProps }: MyAppProps): JSX.Element {
const { ...props } = myProps;
return (
<MyStateProvider {...props}>
<Component {...props} />{' '}
</MyStateProvider>
);
}
And this MyStateProvider in my-state-context.tsx
export const MyStateProvider: React.FC<any> = ( props, {children }) => {
const contextValue = doSomething(props); // <-- trying to accomplish passing props
return (
<MyStateContext.Provider value={contextValue}>
{children}
</MyStateContext.Provider>
);
};
What is the correct way to go about this?
You can do the following:
function App({ Component, ...myProps }: MyAppProps): JSX.Element {
return (
<MyStateProvider {...myProps}>
<Component {...myProps} />
</MyStateProvider>
);
}
and in your provider, supposing that you are passing the component like this <MyStateProvider Children={SomeComponent}/>
// note that first letter of Children should be uppercase to behave as a component
export const MyStateProvider: React.FC<any> = ({Children, ...props}) => {
const contextValue = doSomething({...props});
return (
<MyStateContext.Provider value={contextValue}>
<Children />
</MyStateContext.Provider>
);
};
See the example

Which way is faster and more proper to get information from parent component?

I'm developing an app with Next.js.
I want to get query information via useRouter in Child component.
Which way is faster and more proper to get query information?
Pass props to each ChildComponent from ParentComponent
const ParentComponent = () => {
const router = useRouter()
const { query } = router
return (
<>
<ChildComponent query={query} />
<ChildComponent query={query} />
</>
)
}
const ChildComponent = ({ query }) => {
console.log(query)
return (
<></>
)
}
Get query in each ChildComponent
const ParentComponent = () => {
return (
<>
<ChildComponent />
<ChildComponent />
</>
)
}
const ChildComponent = () => {
const router = useRouter()
const { query } = router
console.log(query)
return (
<></>
)
}

How to access props in a functional HOC?

I have code similar to the following:
const HOC = (props, WrappedComponent) => {
return (
<>
<WrappedComponent />
<Icon className="props.iconStyles" />
</>
);
};
This is not actually valid code, but you can hopefully see what I'm trying to accomplish. I want to be able to use it in the following way:
<HOC iconStyles="icon-stuff">
<TheComponentIWantToWrap>
</HOC>
How can I write this correctly, so as to be able to pass props through? I think I might need to be using children here too, not sure.
It would be something like this.
const HOC = (WrappedComponent) => {
const MyComponent = props => {
return (
<>
<WrappedComponent {...props} />
<Icon className={props.iconStyles} />
</>
);
}
return MyComponent;
};
An HOC is a funtion which returns a Component and usually inject some props on it. You should separate concerns. An actual HOC should look like this
const withFoo = Component =>{
return props =>{
return <Component injectedProps='foo' />
}
}
And be called like this
const Component = withFoo(({ injectedProps }) =>{
return <jsx />
})
If you want to merge arbitrary props as well try to spread the props passed to Component
const withFoo = Wrapped =>{
return props =>{
return <Wrapped injectedProps='foo' {...props}/>
}
}
<Component propsToBeSpreaded='bar' />
If you prefer you can create an aditional layer.
HOC injects some props in a Container
Container accepts arbitrary props
Container renders children
The code
const withHoc = Container =>{
return () => <Container injected='foo' />
}
const Container = withHoc(({ children, injected, ...aditionalProps}) =>{
return(
<div {...aditionalProps}>
{ children }
</div>
)
})
And call it like
const FinalComp = () =>{
return <Container foo='foo'> <span /> </Container>
}
A higher-order component would return another component (another function in this case). A component is a function that returns JSX.
const HOC = (Component) => {
return function WrappedComponent(props) {
return (
<>
<Component {...props} />
<Icon className="props.iconStyles" />
</>
);
};
};
You could still pass down props along with a Component to be enhanced (as per your original approach which you think is wrong) It is right since -
HOCs are not part of React API. They emerge from React's nature of composition.
So your HOC usage is -
const EnhancedComponent = higherOrderComponent(WrappedComponent, anyProps);
Points to note:
Your HOC takes in a Component and returns a Component (enhanced or not)
const higherOrderComponent = (WrappedComponent, anyProps) => {
return class extends React.Component {
// Component body
}
}
Don’t Mutate the Original Component. Use Composition.
Pass Unrelated Props Through to the Wrapped Component.
const higherOrderComponent = (WrappedComponent, anyProps) => {
return class extends React.Component {
render() {
const { HOCProp, ...compProps } = this.props;
return (
<WrappedComponent
...compProps,
someProp={anyProps.someProp}
/>
);
}
}
}
Considering all this, your HOC would look like this -
const withWrappedIcon = (CompToWrap, iconStyle) => {
return (
<Icon className={iconStyle}>
<CompToWrap />
</Icon>
);
}

passing multiple props to component (context API)

I have two <AuthConsumer>,<PeopleConsumer>
and It is belongs to HOC like this:
const withAuth = WrappedComponent => {
return () => (
<AuthConsumer>{props => { console.log(props); return <WrappedComponent auth={props} />}}</AuthConsumer>
);
};
Using like this is works I can get auth as a props.
export default withAuth(withRouter(LoginPage));
but, when I tired export default withPeople(withAuth(withRouter(LoginPage))); is not works I can't get auth, people as props.
So I looked up official document it says:
use like this to passing multiple props from contextAPI
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
So I tried this, but looks ugly:
const withTest = WrappedComponent => {
return () => (
<AuthConsumer>
{auth => (
<PeopleConsumer>
{people => (
<WrappedComponent auth={auth} people={people} />
)}
</PeopleConsumer>
)}
</AuthConsumer>
)
}
In my case is there are better way to providing multiple props?
Please let me know if you need info.
Thank you.
const withAuth = WrappedComponent => {
return (props) => (
<AuthConsumer>{auth => { console.log(props, auth); return <WrappedComponent {...props} auth={auth />}}</AuthConsumer>
);
};

Resources