简体   繁体   中英

Re-Render in component on State Change in Redux Store

I wanted to show logoff button after user signs in and hide visibility of button after the user logged out. Menu items are coming from Menu.js file.

The current workflow of the app is, if user signed in, the auth state is updated in the store, based on that state, I wanted to show a logoff Button in Menu.

The auth state is correctly updated in Redux store on login and I can get the value of auth in Menu.js File from Redux store, but the change is not rendered in Siderbar.js until i refresh the page. What should i need to do in sidebar.js to fetch the menu on auth state change in Store.

Siderbar.js

<pre><code>
import React, { Component } from 'react';
import { withNamespaces, Trans } from 'react-i18next';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';
import { Collapse, Badge } from 'reactstrap';
import SidebarRun from './Sidebar.run';
import SidebarUserBlock from './SidebarUserBlock';

import Menu from '../../Menu';

/** Component to display headings on sidebar */
const SidebarItemHeader = ({item}) => (
    <li className="nav-heading">
        <span><Trans i18nKey={item.translate}>{item.heading}</Trans></span>
    </li>
)

/** Normal items for the sidebar */
const SidebarItem = ({item, isActive}) => (
    <li className={ isActive ? 'active' : '' }>
        <Link to={item.path} title={item.name}>
            {item.label && <Badge tag="div" className="float-right" color={item.label.color}>{item.label.value}</Badge>}
            {item.icon && <em className={item.icon}></em>}
            <span><Trans i18nKey={item.translate}>{item.name}</Trans></span>
        </Link>
    </li>
)

/** Build a sub menu with items inside and attach collapse behavior */
const SidebarSubItem = ({item, isActive, handler, children, isOpen}) => (
    <li className={ isActive ? 'active' : '' }>
        <div className="nav-item" onClick={ handler }>
            {item.label && <Badge tag="div" className="float-right" color={item.label.color}>{item.label.value}</Badge>}
            {item.icon && <em className={item.icon}></em>}
            <span><Trans i18nKey={item.translate}>{item.name}</Trans></span>
        </div>
        <Collapse isOpen={ isOpen }>
            <ul id={item.path} className="sidebar-nav sidebar-subnav">
                { children }
            </ul>
        </Collapse>
    </li>
)

/** Component used to display a header on menu when using collapsed/hover mode */
const SidebarSubHeader = ({item}) => (
    <li className="sidebar-subnav-header">{item.name}</li>
)

class Sidebar extends Component {

    state = {
        collapse: {},
        isLoggedIn: ''
    }

    componentDidMount() {
        // pass navigator to access router api
        SidebarRun(this.navigator.bind(this));
        // prepare the flags to handle menu collapsed states
        this.buildCollapseList()
    }

    /** prepare initial state of collapse menus. Doesnt allow same route names */
    buildCollapseList = () => {
        let collapse = {};

        Menu
            .filter(({heading}) => !heading)
            .forEach(({name, path, submenu}) => {
                collapse[name] = this.routeActive(submenu ? submenu.map(({path})=>path) : path)
            })
        this.setState({collapse});
    }

    navigator(route) {
        this.props.history.push(route);
    }

    routeActive(paths) {
        paths = Array.isArray(paths) ? paths : [paths];
        return paths.some(p => this.props.location.pathname.indexOf(p) > -1)
    }

    toggleItemCollapse(stateName) {
        for (let c in this.state.collapse) {
            if (this.state.collapse[c] === true && c !== stateName)
                this.setState({
                    collapse: {
                        [c]: false
                    }
                });
        }
        this.setState({
            collapse: {
                [stateName]: !this.state.collapse[stateName]
            }
        });
    }

    getSubRoutes = item => item.submenu.map(({path}) => path)

    /** map menu config to string to determine what element to render */
    itemType = item => {
        if (item){
        // console.log(item)
            if (item.heading) return 'heading';
            if (!item.submenu) return 'menu';
            if (item.submenu) return 'submenu';
    }}

    render() {
        return (
            <aside className='aside-container'>
                { /* START Sidebar (left) */ }
                <div className="aside-inner">
                    <nav data-sidebar-anyclick-close="" className="sidebar">
                        { /* START sidebar nav */ }
                        <ul className="sidebar-nav">
                            { /* START user info */ }
                            <li className="has-user-block">
                                <SidebarUserBlock/>
                            </li>
                            { /* END user info */ }

                            { /* Iterates over all sidebar items */ }
                            {
                                Menu.map((item, i) => {
                                    // heading
                                    if(this.itemType(item) === 'heading')
                                        return (
                                            <SidebarItemHeader item={item} key={i} />
                                        )
                                    else {
                                        if(this.itemType(item) === 'menu')
                                            return (
                                                <SidebarItem isActive={this.routeActive(item.path)} item={item} key={i} />
                                            )
                                        if(this.itemType(item) === 'submenu')
                                            return [
                                                <SidebarSubItem item={item} isOpen={this.state.collapse[item.name]} handler={ this.toggleItemCollapse.bind(this, item.name) } isActive={this.routeActive(this.getSubRoutes(item))} key={i}>
                                                    <SidebarSubHeader item={item} key={i}/>
                                                    {
                                                        item.submenu.map((subitem, i) =>
                                                            <SidebarItem key={i} item={subitem} isActive={this.routeActive(subitem.path)} />
                                                        )
                                                    }
                                                </SidebarSubItem>
                                            ]
                                    }
                                    return null; // unrecognized item
                                })
                            }
                        </ul>
                        { /* END sidebar nav */ }
                    </nav>
                </div>
                { /* END Sidebar (left) */ }
            </aside>
        );
    }
}

Sidebar.propTypes = {
    isLoggedIn: PropTypes.object.isRequired
};
const mapStateToProps = (state, ownProps) => {
    // console.log("Sidebar: ", state.auth);
    return{
        isLoggedIn: state.auth,
}}

export default connect(mapStateToProps)(withRouter(Sidebar));

What should i do in Sidebar.js, that it re-renders the menu on auth state change.

Any help would be highly appreciated.

Menu.js File



    import configureStore from './store/store';
    const store = configureStore();

    function select(state) {
        return state.auth
    }

        let loggedIn = select(store.getState());

        let Meu = [

        {
            name: 'Analytics',
            icon: 'icon-speedometer',
            translate: 'sidebar.nav.DASHBOARD',
            submenu: [{
                    name: 'Overview',
                    path: '/dashboard',
                    translate: 'sidebar.nav.element.HOME'
                },
                {
                    name: 'Revenue',
                    path: '/revenue',
                    translate: 'sidebar.nav.element.REVENUE'
                },
                {
                    name: 'Source',
                    path: '/source',
                    translate: 'sidebar.nav.element.SOURCE'
                },
                {
                    name: 'Marketing',
                    path: '/marketing',
                    translate: 'sidebar.nav.element.MARKETING'
                }
            ]
        },
        {
            name: 'Tools',
            icon: 'fa fa-briefcase',
            translate: 'sidebar.nav.TOOLS',
            submenu: [
                {
                    name: 'Customer Service',
                    path: '/customer-service',
                    translate: 'sidebar.nav.element.CUSTOMER-SERVICE'
                }

            ]
        },

        {
            name: 'Settings',
            icon: 'icon-settings',
            translate: 'sidebar.nav.SETTINGS',
            submenu: [{
                    name: 'Profile Settings',
                    path: '/profile',
                    translate: 'sidebar.nav.element.PROFILE-SETTINGS'
                },
                {
                    name: 'Company Settings',
                    path: '/company-settings',
                    translate: 'sidebar.nav.element.SETTINGS'
                }
            ]
        },

        {(loggedIn.authResponse) ?
            ({
                name: 'Logoff',
                icon: 'icon-lock',
                path: '/logoff' 
            }) : null
        }
    ];
        const Menu = Meu.filter(word => word !== null) 
        export default (Menu)

Inside the parent component of your log out button you can conditionally render the log out button; take note this means your parent component is connected to redux via connect(mapStateToProps)

{ this.props.isLoggedIn ? <Button>Log Out</Button> : null }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM