As the tittle says, I would like to be able to use the same axios response for differents components. I have some restrictions like, I\'m onlyl able to use react by adding scrip
Here's a working example of how you might do it. I've tried to annotate everything with comments, but I'm happy to try to clarify if you have questions.
// 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")
);