简体   繁体   中英

ReactJS Preload Navigation Elements

I recently moved my static navigation bar from being a general html element to included in my React rendering for my page because I wanted to incorporate the ability to dynamically load notifications in a modal that can get triggered in the navigation. With this change, I have noticed that my navigation bar does not appear immediately when the page is loaded, but when componentDidMount() { this.fetchList(); } componentDidMount() { this.fetchList(); } finishes loading.

I personally belief that this is because the navigation component is being set in the render() call involved with this API fetch and since this class is being set after the call is made, then the navigation will have to wait until the fetch comes back successfully or as a failure.

If this is true, does that mean that I need to set my navigation at a higher level to ensure it loads when the page loads with styling and non-react elements?

Here is my ReactDOM.render() :

import React from 'react';
import ReactDOM from 'react-dom';
import AnnotationFeedContainer from './components/app/activity-feed/activity-feed.js';


ReactDOM.render(<AnnotationFeedContainer />, document.getElementById('annotation-card'));

Here is <AnnotationFeedContainer /> which is rendering my react elements ( <Navigation /> is the component I am looking to load before and regardless of fetchList() ):

import React from 'react';
import fetch from 'node-fetch';
import path from 'path';
import Navigation from '../navigation';
import AnnotationSearchForm from './annotation-search-form';
import OnboardingInformation from './onboarding/information';
import ActivityFeedNotifications from './notifications/notifications';
import AnnotationFeed from './annotation-card/annotation-card-feed';
import { API_ROOT } from '../config/api-config';

//GET /api/test and set to state
export default class AnnotationFeedContainer extends React.Component{
    constructor(props, context) {
        super(props, context);
        this.state = this.context.data || window.__INITIAL_STATE__ || { annotations: [], isLoading: true, onboardingWelcome: false, notifications: [] };
    }

    fetchList() {
            fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
                .then(res => {
                    return res.json();
                })  
                .then(data => {
                    console.log(data);
                    this.setState({ annotations: data.annotation, user: data.user, csrf: data.csrfToken, isLoading: false, onboardingWelcome: data.onboardingWelcome, notifications: data.notifications, feedPreference: data.feedPreference });
                }) 
                .catch(err => {
                    console.log(err);
                });
    }

    componentDidMount() {
        this.fetchList();
    }

    render() {
        if(this.state.feedPreference === 1){    
            return (
                <div>
                    <Navigation notifications={this.state.notifications}/>
                    <AnnotationSearchForm />
                    <div className="activity-feed-container">
                        <div className="container">
                            <OnboardingInformation onboarding={this.state.onboardingWelcome}/>
                            <LoadingIndicator loading={this.state.isLoading} />
                            <div className="row">
                                <div className="col-md-12">
                                    <AnnotationFeed {...this.state} />
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            )
        } else {
            return (
                <div className="activity-feed-container">
                    <div className="container">
                        <OnboardingInformation onboarding={this.state.onboardingWelcome}/>
                        <LoadingIndicator loading={this.state.isLoading} />
                        <div className="row">
                            <div className="col-md-6 col-md-offset-3">
                                <AnnotationFeed {...this.state} />
                            </div>
                            <div className="col-md-1 col-md-offset-1">
                                <ActivityFeedNotifications notifications={this.state.notifications} />
                            </div>
                        </div>
                    </div>
                </div>
            )
        }
    }
};

//Loading Indicator
const LoadingIndicator = props => {
    if(props.loading == true){
        return (
            <div className="spinner">
                <div className="bounce1"></div>
                <div className="bounce2"></div>
                <div className="bounce3"></div>
              <p>Loading...</p>
            </div>
        )
    } else {
        return null;
    }
}

Navigation Component:

import React from 'react';
import NotificationPopover from './activity-feed/notifications/notifications-popover';

//Navigation
export default class Navigation extends React.Component {
    render() {
        return (
            <nav className="navbar">
                <div className="container-fluid">
                    <div className="navbar-header">
                    <button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navigationLinks">
                        <span className="icon-bar mobile-nav-toggle"></span>
                        <span className="icon-bar mobile-nav-toggle"></span>
                        <span className="icon-bar mobile-nav-toggle"></span>
                    </button>
                        <a className="navbar-brand" href="/app"><img src="/images/synotate_logo.svg" className="nav-logo-svg"></img></a>
                    </div>
                    <div className="collapse navbar-collapse" id="navigationLinks">
                        <ul className="nav navbar-nav">
                            <li className="nav-item">
                                <a className="nav-link" href="/app">Activity Feed</a>
                            </li>
                            <li className="nav-item">
                                <a className="nav-link" href="/app/settings">Settings</a>
                            </li>
                        </ul>
                        <ul className="nav navbar-nav navbar-right">
                            <li className="nav-item">
                                <NotificationPopover notifications={this.props.notifications}/>
                            </li>
                            <li className="nav-item">
                                <a className="nav-link" href="/app/logout">Log Out</a>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
        )
    }
}

in your <AnnotationFeedContainer> you have this line if(this.state.feedPreference === 1) in the render() method.

For that condition wont be true unless you have a successful fetch() event coming from fetchList() in componentDidMount() , and if this condition returns true , you will render what ever is inside the braces of it, which includes the <Navigation> component

Else-wise you will render another code, which is loading indicator I guess, and in here, you didnt include your Navigation component, thus it won't show.

That is the logic you are using, you are telling your app to not include the Navigation component unless it fetches ur data, which happens to be logically fine! if you want to display it other wise, you may wanna be moving it out of the if statement you have

It seems like you fetch call is responsible for setting the feedPreference variable in your state.

Since this variable is used in your if condition, and the <Navigation/> component isnt rendered when the feedPreference isn't set.

A simple solution would be to add <Navigation/> into the else condition (in the <AnnotationFeedContainer/> 's render function) :

    } else {
        return (
            <>
                <Navigation notifications={this.state.notifications}/>
                <div className="activity-feed-container">
                    <div className="container">
                        <OnboardingInformation onboarding={this.state.onboardingWelcome}/>
                        <LoadingIndicator loading={this.state.isLoading} />
                        <div className="row">
                            <div className="col-md-6 col-md-offset-3">
                                <AnnotationFeed {...this.state} />
                            </div>
                            <div className="col-md-1 col-md-offset-1">
                                <ActivityFeedNotifications notifications={this.state.notifications} />
                            </div>
                        </div>
                    </div>
                </div>
            </>
        )
    }

A more "React-like" way of doing it could be to replace your entire condition wiht the following :

    return (
        <>
            <Navigation notifications={this.state.notifications} />
            {this.state.feedPreference === 1 && <AnnotationSearchForm />}
            <div className="activity-feed-container">
                <div className="container">
                    <OnboardingInformation onboarding={this.state.onboardingWelcome} />
                    <LoadingIndicator loading={this.state.isLoading} />
                    <div className="row">
                        {this.state.feedPreference === 1 ?
                            <>
                                <div className="col-md-6 col-md-offset-3">
                                    <AnnotationFeed {...this.state} />
                                </div>
                                <div className="col-md-1 col-md-offset-1">
                                    <ActivityFeedNotifications notifications={this.state.notifications} />
                                </div>
                            </>
                            :
                            <div className="col-md-12">
                                <AnnotationFeed {...this.state} />
                            </div>
                        }
                        <div className="col-md-6 col-md-offset-3">
                            <AnnotationFeed {...this.state} />
                        </div>
                        <div className="col-md-1 col-md-offset-1">
                            <ActivityFeedNotifications notifications={this.state.notifications} />
                        </div>
                    </div>
                </div>
            </div>
        </>
    )

Using inline ifs ( && ) allows you to avoid repetitions. If you cannot use fragments ( <> they were added in the latest React version) you can replace them with <div> tags

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