Re-Render in component on State Change in Redux Store

时间:2019-01-18 18:09:39

标签: javascript reactjs react-redux

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)

1 个答案:

答案 0 :(得分:0)

在“注销”按钮的父组件内部,您可以有条件地呈现“注销”按钮;请注意,这意味着您的父组件通过connect(mapStateToProps)连接到redux

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