Kid
Kid

Reputation: 2205

Calling this react HOC feels like i could abstract this in another way please advice

I'm a beginner and need advice. I learn Reactjs and created this HOC so I could navigate in the Router V6. My concern is that from my desktop Navbar and from the mobile side menu I have links to the same Dashboard.

So I create a HOC since it's the same code running in both locations and the HOC now have this code.

This is the HOC:

import React from 'react';
import AuthUserContext from './context';
import * as ROLES from '../constants/roles';

const WithDashboardNavigate = Component => {
    class WithDashboardBase extends React.Component {
        constructor() {
            super();
            this.onDashboard = this.onDashboard.bind(this);
        }

        onDashboard = navigate => {
            const { authUser } = this.props;
            if (authUser) {
                if (authUser.roles.includes(ROLES.ANON)) {
                    navigate('/app/login');
                } else if (authUser.roles.includes(ROLES.USER) || authUser.roles.includes(ROLES.ADMIN)) {
                    const view = localStorage.getItem('currentDashBoardView');
                    // Here if user has used nav menu earlier then that last view is loaded
                    if (view) navigate(view);
                    // or default to dash
                    else navigate('/app/dashboard');
                }
            }
        };

        render() {
            return <Component dashboardNavigater={this.onDashboard} {...this.props} />;
        }
    }

    const WithDashboard = () => (
        <AuthUserContext.Consumer>
            {authUser => (
                <div>
                    <WithDashboardBase authUser={authUser} />
                </div>
            )}
        </AuthUserContext.Consumer>
    );

    return WithDashboard;
};

export default WithDashboardNavigate;

And I use it like this from Navbar:

function SignedInButton(props) {
    const navigate = useNavigate();

    function openDashboard() {
        props.dashboardNavigater(navigate);
    }

    return (
        <div>
            <Button className="button is-large" onClick={openDashboard}>
                <span className="icon is-medium">
                    <i className="fas fa-user" />
                </span>
            </Button>
        </div>
    );
}

export default WithDashboardNavigate(SignedInButton);

And I use it like this from SideMenu:

/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import WithDashboardNavigate from '../../session/WithDashboardNavigate';

function SideMenu(props) {
    const { close, dashboardNavigater } = props;
    const navigate = useNavigate();

    const onProfilePageClick = () => {
        if (close) close();
        dashboardNavigater(navigate);
    };

    return (
        <div>
            <MenuLinksSpace />
            <MenuLinks>
                <li>
                    <a onClick={onProfilePageClick} role="presentation">
                        <span className="icon is-medium">
                            <i className="fas fa-user" />
                        </span>{' '}
                        Dashboard
                    </a>
                </li>
            </MenuLinks>
        </div>
    );
}

My question is I'm I overdoing this? I mean is there an easier way to do this conforming better to React best praxis?

Upvotes: 1

Views: 55

Answers (1)

Rahul Sharma
Rahul Sharma

Reputation: 10071

It's totally dependent on the requirement and how you design. My opinion is that if you using function components you don't need to mix with class components and bind(this).

Note: WithDashboard function you are not passing props if any component passes props that are not passed to WithDashboardBase.

import React from 'react';
import AuthUserContext from './context';
import * as ROLES from '../constants/roles';

const WithDashboardNavigate = Component => {
    const WithDashboardBase = (props) => {
       const onDashboard = navigate => {
            const { authUser } = props;
            if (authUser) {
                if (authUser.roles.includes(ROLES.ANON)) {
                    navigate('/app/login');
                } else if (authUser.roles.includes(ROLES.USER) || authUser.roles.includes(ROLES.ADMIN)) {
                    const view = localStorage.getItem('currentDashBoardView');
                    // Here if user has used nav menu earlier then that last view is loaded
                    if (view) navigate(view);
                    // or default to dash
                    else navigate('/app/dashboard');
                }
            }
        };

        return <Component dashboardNavigater={onDashboard} {...props} />;
    }

    const WithDashboard = (props) => (   // Pass props from here as well 
        <AuthUserContext.Consumer>
            {authUser => (
                <div>
                    <WithDashboardBase authUser={authUser} {...props} />
                </div>
            )}
        </AuthUserContext.Consumer>
    );

    return WithDashboard;
};

export default WithDashboardNavigate;

I have created connect HOC for context API(same as redux). Check this it'll help you understand props pass from parent to child.

https://stackblitz.com/edit/reactjs-usecontext-usereducer-state-management?file=src%2FStore.js

Upvotes: 3

Related Questions