I am not sure if I am doing this right but I want to make a hoc that will display components based on if the user is admin. This is my HOC:
export const WithUser = (Component) => (props) => {
if (props.admin) {
return <Component {...props} />;
}
return null;
};
There is an error when I use it Functions are not valid as a React child. It doesnt make sense to me.
I want to use it like this:
<WithUser admin>
<button>Hi</button>
</WithUser>
But I get that error. Maybe Im not using HOCs correctly. I just want to wrap components that the user need to be an admin to view
you can take a look HOC document from react official website
modify your code to this:
// admin prop should be obtained from within WithUser or other places such as redux store or react context
export const WithUser = (Component) => (props) => {
if (props.admin) {
return <Button {...props} />;
}
return null;
};
class Button extends React.Component {
render() {
return <button>Hi</button>
}
}
export default WithUser(Button)
WithUser is not a component. It is a function that takes a component and returns a modified component. That's what a Higher-Order-Component (HOC) is.
You can write a component that does what you want with your desired syntax:
<WithUser admin>
<button>Hi</button>
</WithUser>
But it won't be an HOC. Instead you want to use the children prop to access the child JSX elements and conditionally render them.
export const WithUser = ({admin, children}) => {
return admin ? <>{children}</> : null;
};
Related
I have a component which is going through an hoc, but i want to get some props of this component inside the hoc. All works fine but i can not find out how to get the props out of this child component into the hoc.
here is the component which is going through the hoc, and that is this 'getAction' props i want to extract in the hoc
class ProjectPage2 extends Component {
render() {
return (
<Project2 getAction="getAction"/>
);
};
};
export default PageHandler(ProjectPage2)
here is the hoc component (imported as PageHandler in the ProjectPage2)
export default (ChildComponent) => {
class ComposedComponent extends Component {
render() {
// here i want to get the 'getAction' props, which is inside this ChildComponent
// because i need to use it into this hoc logic
return <ChildComponent {...this.props} />;
}
}
const mapStateToProps = (state) => {
return {
comments: state.project2
}
};
const loadData = (store) => {
return store.dispatch(getProject2());
};
return {
loadData,
component: connect(mapStateToProps, { getProject2 })(ComposedComponent)
}
};
if some one have an idea it would be great. Thanks.
I think you're very close already. It looks to me like you want the final result to be something like:
<Project2 getAction="getAction" comments={...} />
But what ends up getting rendered is just:
<Project2 getAction="getAction" />
See, the custom props of your HOC are passed to your child component via the child's props. You aren't using those, so the child just completely ignores the HOC. You can fix this with:
class ProjectPage2 extends Component {
render() {
return (
<Project2 getAction="getAction" {...this.props} />
);
};
}
Thank you for the answer. But it s not what i was looking for. I wanted to get into the HOC some children component's props passing through. But i finally get the idea which solved it. In fact it s so simple......
i wanted to pass the "getAction" string into the HOC. But i didn t find any solution to extract it (from the passing through component) there.
The solution is simply to pass it into the export default
class ProjectPage2 extends Component {
render() {
return (
// i was trying to use the component props
// <Project2 getAction="getAction"/>
// but no need
<Project2 />
);
};
};
// pass it into the fonction fix it
export default PageHandler(ProjectPage2, "getAction")
then get it in the HOC
export default (ChildComponent, varAction) => {
class ComposedComponent extends Component {
console.log(varAction) // return getAction
I'm new to react development. I often came accross code examples where a component takes other component as props, like so:
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import { UserInfo } from './userinfo';
const UserPanel = (props) => {
return (
<UserInfo icon={<AccountCircleIcon/>} user={props.user}>
{props.children}
</UserInfo>
);
}
Other time, I see component that takes class name as one of its props. Like so:
import { Admin } from 'react-admin';
import { AppLayout } from './components';
const App = (props) => {
return (
<Admin layout={AppLayout}>
{* other codes here ... *}
</Admin>
);
}
What's the difference? If both approach can be used to achieve the same effect, which one is considered better?
The difference is that on the first code, you're calling the render function on your component and passing the result render as a prop. On the second code, you're sending the component (not rendered yet!) as a prop and will use it to render in another place of your tree.
Essentially they're the same and it's up to how you want to drive passing props on your project.
Just to illustrate, consider that you have two components:
// Returns a JSX.Element
function AccountCircleIcon() {
return (<>Foo</>)
}
// Also, returns a JSX.Element
function AppLayout() {
return (<>Bar</>)
}
function App() {
return (
<>
// The two lines below will produce exactly a JSX.Element
<AccountCircleIcon/>
{AppLayout()}
// Dependency prop here will be a valid React component
// Like a function you can call: AppLayout()
<AnotherComponent dependency={AppLayout}/>
// Dependency prop will be also valid React component
// but it's already rendered.
<AnotherComponent dependency={<AccountCircleIcon/>}/>
</>
)
}
Check this out to understand and play a little bit with the idea.
I'm using React and Redux with react-redux, and I'm creating in React a High order Component that I want to connect to the Redux store, like this:
const HoC = parameter => WrappedComponent =>
return class WithSelected extends Component {
// ..some use of 'parameter'
render() {
return <WrappedComponent />
}
[...]
const exportComponent = compose(
connect(mapStateToProps),
HoC
)
export default exportComponent;
and
import Component from '../Component'
import exportComponent from '../file'
const componentToExport = exportComponent('someValue')(Component);
Now, this approach gives this error:
TypeError: Object(...) is not a function
Btw if I don't use currying creating the Hoc, it works, like this:
const HoC = (parameter, WrappedComponent) => [etc]
and
import Component from '../Component'
import exportComponent from '../file'
const componentToExport = exportComponent('someValue', Component);
It works. But how can I use currying creating HoC in React and use Redux at the same time?
There is nothing wrong with currying an HOC. The following is a valid high order component
const withHoc = param => Component => props =>{
return <Component {...props} foo={param} />
}
Your problem is compose. Your right-most argument which will provide the signature for the resulting composed function is actually evaluating to another HOC not the component itself.
You should connect the returned component
const HoC = parameter => WrappedComponent => connect(...)(props =>{
/*...*/
})
Returning a class based component is the same as returning a functional one
const HoC = parameter => WrappedComponent => connect(...)(class X extends Component{
render(){ return <div /> }
})
Just isn't that pretty though
I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)
I'm building a Web components library where I have a series of components which ideally inherit / takes advantage of BaseComponent. After some reading, inheritance isn't recommended in React and instead I could use forwardRef? or probably higher-order components? The only thing, I'm not very familiar with this concept and couldn't find good examples, tutorial specific for my case. Hopefully someone can tell me how to approach this, what's best-practice?
One of technique I have in mind for the BaseComponent is leveraging IntersectionObserver to trigger animations. As you can imagine, I don't want to put this logic in multiple places. For the sick of having a basic example, below I simply have a click event listener on the BaseComponent:
class Image extends React.Component {
render() {
return (
<div>
<div>
<img className={className} src={src} alt={alt} />
</div>
</div>
);
}
}
export default Image;
// export default withMagic(Image); ??
class BaseComponent extends React.Component {
withMagic() {
}
componentDidMount() {
{/* ref should be <img> DOM element */}
ref.addEventListener("click", this.handleClick);
}
}
export BaseComponent();
Thanks
HoC probably the better solution
// 2. WrappedComponent
export default WrappedComponent => {
// If u want to deal with class
class NewComponent extends React.Component {
//3
handleClick = () => {
// Your Events
}
render () {
//4
return <WrappedComponent {...this.props} handleClick={this.handleClick} />
}
}
// If u want to deal with functional Component
const NewComponent = props => {
const handleClick = () => // your events
return <WrappedComponent {...props} handleClick={handleClick} />
}
return NewComponent
}
How to use ?
import withClick from 'path/withClick'
const A = props => {
return (
//4
<button onClick={props.handleClick}>Click here</button>
)
}
// 1.
export default withClick(A)
How this magic work ?
U are using withClick method by passing A component as a params
A component is named as WrappedComponent inside withClick function
inside the withClick function, u create a new component with your desired handler, logic or even state, then pass them as a props into WrappedComponent
after that, ur current component will have these handler or logic
If u want to pass params, u can use Higher order Function that returns Higher Order Component like
export default (params1, params2, ...paramsn) => WrappedComponent => {
// remain like the same
}