// Fake response object for the store's "load" request
const fakeResponse = {
  user: {
    fullName: "Carolina Ponce",
    roles: [
      { roleName: "administrator" },
      { roleName: "editor" },
      { roleName: "moderator" },
      { roleName: "generally awesome person" }
    ]
  }
};
// this class is responsible for loading the data
// and making it available to other components.
// we'll create a singleton for this example, but
// it might make sense to have more than one instance
// for other use cases.
class UserStore {
  constructor() {
    // kick off the data load upon instantiation
    this.load();
  }
  // statically available singleton instance.
  // not accessed outside the UserStore class itself
  static instance = new this();
  // UserStore.connect creates a higher-order component
  // that provides a 'store' prop and automatically updates
  // the connected component when the store changes. in this
  // example the only change occurs when the data loads, but
  // it could be extended for other uses.
  static connect = function(Component) {
    // get the UserStore instance to pass as a prop
    const store = this.instance;
    // return a new higher-order component that wraps the connected one.
    return class Connected extends React.Component {
      // when the store changes just force a re-render of the component
      onStoreChange = () => this.forceUpdate();
      // listen for store changes on mount
      componentWillMount = () => store.listen(this.onStoreChange);
      // stop listening for store changes when we unmount
      componentWillUnmount = () => store.unlisten(this.onStoreChange);
      render() {
        // render the connected component with an additional 'store' prop
        return React.createElement(Component, { store });
      }
    };
  };
  // The following listen, unlisten, and onChange methods would
  // normally be achieved by having UserStore extend EventEmitter
  // instead of re-inventing it, but I wasn't sure whether EventEmitter
  // would be available to you given your build restrictions.
  // Adds a listener function to be invoked when the store changes.
  // Called by componentWillMount for connected components so they
  // get updated when data loads, etc.
  // The store just keeps a simple array of listener functions. This
  // method creates the array if it doesn't already exist, and
  // adds the new function (fn) to the array.
  listen = fn => (this.listeners = [...(this.listeners || []), fn]);
  // Remove a listener; the inverse of listen.
  // Invoked by componentWillUnmount to disconnect from the store and
  // stop receiving change notifications. We don't want to attempt to
  // update unmounted components.
  unlisten = fn => {
    // get this.listeners
    const { listeners = [] } = this;
    // delete the specified function from the array.
    // array.splice modifies the original array so we don't
    // need to reassign it to this.listeners or anything.
    listeners.splice(listeners.indexOf(fn), 1);
  };
  // Invoke all the listener functions when the store changes.
  // (onChange is invoked by the load method below)
  onChange = () => (this.listeners || []).forEach(fn => fn());
  // do whatever data loading you need to do here, then
  // invoke this.onChange to update connected components.
  async load() {
    // the loading and loaded fields aren't used by the connected
    // components in this example. just including them as food
    // for thought. components could rely on these explicit fields
    // for store status instead of pivoting on the presence of the
    // data.user object, which is what the User and Role components
    // are doing (below) in this example.
    this.loaded = false;
    this.loading = true;
    try {
      // faking the data request. wait two seconds and return our
      // hard-coded data from above.
      // (Replace this with your network fetch.)
      this.data = await new Promise(fulfill =>
        setTimeout(() => fulfill(fakeResponse), 2000)
      );
      // update the loading/loaded status fields
      this.loaded = true;
      this.loading = false;
      // call onChange to trigger component updates.
      this.onChange();
    } catch (e) {
      // If something blows up during the network request,
      // make the error available to connected components
      // as store.error so they can display an error message
      // or a retry button or whatever.
      this.error = e;
    }
  }
}
// With all the loading logic in the store, we can
// use a much simpler function component to render
// the user's name.
// (This component gets connected to the store in the
// React.createElement call below.)
function User({ store }) {
  const { data: { user } = {} } = store || {};
  return React.createElement(
    "span",
    { className: "mr-2 d-none d-lg-inline text-gray-600 small" },
    user ? user.fullName : "loading (User)…"
  );
}
ReactDOM.render(
  // Connect the User component to the store via UserStore.connect(User)
  React.createElement(UserStore.connect(User), {}, null),
  document.getElementById("userDropdown")
);
// Again, with all the data loading in the store, we can
// use a much simpler functional component to render the
// roles. (You may still need a class if you need it to do
// other stuff, but this is all we need for this example.)
function Roles({ store }) {
  // get the info from the store prop
  const { data: { user } = {}, loaded, loading, error } = store || {};
  // handle store errors
  if (error) {
    return React.createElement("div", null, "oh noes!");
  }
  // store not loaded yet?
  if (!loaded || loading) {
    return React.createElement("div", null, "loading (Roles)…");
  }
  // if we made it this far, we have user data. do your thing.
  const roles = user.roles.map(rol => rol.roleName);
  return React.createElement(
    "a",
    { className: "dropdown-item" },
    roles.join(", ")
  );
}
ReactDOM.render(
  // connect the Roles component to the store like before
  React.createElement(UserStore.connect(Roles), {}, null),
  document.getElementById("dropdownRol")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="userDropdown"></div>
<div id="dropdownRol"></div>