So I had this thought occur to me today. I've been learning React for a bit, and I often hear the Context API as a shared data source for nested components. You instantiate a context, wrap all the components you want to have access to in that context's provider, and do what you need to with that data inside each component.
My question then is essentially, why do we use the Context API instead of just using a shared reference to a value inside a module? At the end of the day the useContext hook and Context.Consumer require an amount of code roughly equivalent to simply importing a module across all the components that need access to its data.
I'm struggling to understand how using the Context API actually solves any problems that couldn't just as easily be solved with what the browser already provides us, so what am I missing here?
Related
Background
Consider the following:
// app.js
import API from 'utils/API';
const api = new API(config.app.urls.api, endpoints, token);
app.provide('$api', api);
Based on everything I have read in the past 24 hours, the above is the advised method of providing a 'service' to Vue.js 3.
However, the issue with the above is that I now cannot access the API instance as the inject method cannot be used outside of a Vue component.
As such, when I want to import the API instance into my Vuex modules, I can't...
Question
Is there an 'advised' way of accessing a provided service outside of a Vue component? Or is one of my proposed solutions below how it should be done?
Possible Solutions
Proposed Solution 1
Instead of providing the service to Vue, we can just add it to the global properties like so:
// app.js
app.config.globalProperties.$api = api;
and then we can access it like so in the store:
// some-vuex-module.js
import { getCurrentInstance } from 'vue';
const api = getCurrentInstance().appContext.config.globalProperties.$api;
I am using the above for certain things in my app but for the API service it just seems wrong to do it this way.
Proposed Solution 2
Another solution I thought of was to just make the API instance available to the window like so:
// app.js
import API from 'utils/API';
const api = window.api = new API(config.app.urls.api, endpoints, token);
app.provide('$api', api);
The above seems like an anti-pattern though...
The preferable method in modular environment (which Vue 3 setup is commonly is) is to just import api in places where it's used, regardless of whether it's used inside or outside the component.
The solution with Vue global property originated at the time when Vue applications weren't necessarily modular so they relied on Vue instance as global application scope. Currently it's suitable only for properties that are primarily used in a template and require boilerplate code to import and expose them. The example is $t in vue-i18n.
The solution with provide/inject is usable for cases that need dependency injection. A common case is a library that require dependencies to be loosely coupled. Another one is that a dependency depends on component hierarchy and may vary between components.
The solution with window should be avoided, unless application is divided into separate scripts that cannot interact through common JS modules. Even then the problem is that a script that defines global variable should be loaded before those that use it.
The question is probably more theoretical.
I have little experience with Vue and am trying to figure out where my knowledge gaps are and fill them.
There are standard mechanisms for interaction between components:
from top to bottom - input parameters (props) are passed from parent components to child components
from bottom to top - events are thrown from child to parent
And on the other hand, there is VUEX with its own data storage, which is, roughly speaking, a global variable object with a set of methods for working with it.
Data from this storage is available at any time to any component. And it turns out that the use of Vuex seems to make the standard interaction mechanisms of components completely unnecessary.
Well, perhaps, the generation of events is still needed so that one component can quickly make it clear to the other about the completed action, events, etc.
The question is, does Vuex generally override the standard component interactions?
If it is not, how should it be combined in the right way?
I'll try to answer your question.
Vuex will be very usefull to store data that you'll need in a part of the application or globally, like user data.
If you can simply use $emit or props use it, it will be better and simple to understand the code, because it will be overkill to use the store just for "a prop".
So, you will use Vuex in your component to call an action and fetch / store some data you will need in a another view out of your children/parents context.
I don't know if my explanations are well haha, I tried :)
Yesterday I had an interview for React developer position. Interviewer asked me about global functions and how to create and call global functions with React ?
I thought he is talking about Redux and state containers ...
But he said, no ... it's not about state containers and it's about react services.
Since after interview I searched about react services, but I didn't find any thing.
What do you think about this feature and global functions ? I should import global functions above all components or here is different method to handle this ?
Thanks
You have to describe what you call a "react service" and "global function" here, then it will be possible to say the answer how to get it done.
Any kind of "global" stuff, like the helper function which you need in many different places I would put in separate file and export it there and then import/require anywhere needed.
Traditionally, in the web app, the global function may be something added to the window object. Which is a bad pattern and it's well documented over the years. Just google on a topic.
"React service", for me, mostly reminds some API that can run server-side render to render the react app. Like API endpoint which you provide ReactJS app URL and it will spit back raw HTML. I would use headless chrome and global function in this context may be js running in page context running inside the chrome but this stuff is definitely out of the scope of ReactJS.
Another thing, it could be any kind of helper functions that provide some kind of data management, connection handling to something or almost whatever else.
I think the correct answer would be: Define what do you mean by "react service" and "global function".
Too many things could be called a service or global function.
I'm quite new to React, and I'm making a single page application with React.
So far, I've build the application with components and child components, having their own local state, however the child components doesn't really interact with one another, which is what I want them to, basically, with the least amount of boiler plate code...
The problem I'm facing, is that a change in some child component, should be able to update the state of another child component, somewhere else in the component tree.
A selection in one child component should also be able to trigger a function in another component updating it with data and so on.
I've considered having just one global application state, that all components can call and update when something in them changes, and this one application state will then update other components in the tree. Kinda like having a single "controller" with it's own state, that all components "views" can call, and which updates the states of other components as needed. (I'm used to WPF and MVC style of GUI programming).
What I've considered:
One could try to implement this with callback functions defined in the top of the hierarchy, to be sent down through the hierarchy and called from a child component when it changes.
This method however results in a LOT of boilerplate code that just passes functions to their child components. It feels wrong and hard to maintain...
To avoid all this passing around and boilerplate code, I've tried using a React Context, however this is not working as well as I hoped. I can only access the context from within the render function and from lifecycle functions, and sadly I often get complicated errors that are hard to understand. It seems like I'm exploiting React Context to do something you shouldn't use it for...
I've considered using a singleton pattern in JavaScript, however then that singleton needs to have a reference to the root component, and query for the component it needs to change... This seems like kind of a hack, and may not be that pretty, but idk.
I'm considering trying out React Redux however it seems to work in many ways similar to React Context (I'll be honest, I haven't read much into it yet).
What I need:
I need to ask someone with greater React experience than me: How do you keep a global application state, and update child components based on changes to the global application state? Also: Am I thinking about this all wrong? Am I trying to do something in a non-react way, failing to see how I should do it in React?
You can happily go with Redux or MobX, they're fine.
I suggest Taming The State from Robin Wieruch: https://roadtoreact.com/course-details?courseId=TAMING_THE_STATE
There are the book and the course. He shows different ways of handling React state.
Redux was created specifically for the problem yo stated.
Reacts follows a top-down down-top unidirectional flow in essence. Context API is useful in simple use cases but would fail horribly in a large scale application where you'd be creating consumers everywhere.
I'd suggest investing some time in Redux so that will save your precious time in long run.
There's a reason all big three frameworks require a state management library to be useful for large scale complex apps. (Angular has NgRx and Vue has Vuex).
In front-end apps there is often data which needs to be accessed by many components. Routing is one example, another is configuration data, e.g. feature switches, default language, etc.
In an app that isn't using any particular framework, I might share this configuration data across modules using
export class Configuration {
static getConfig () {
// get the config from the server
return axios.get('/config').then(function (response) {
return response;
})
}
}
Then import this class into any module that needs to access configuration data. With some front-end framework, it's obvious how to share such "global" data, e.g.
AngularJS - Configuration should be defined as a service that's dependency-injected into any controllers/directives/services that need to access config. data
Vue.js - use a mixin
However, when using ReactJS it's not obvious which approach should be used. Possible options are:
A plain-old JavaScript module. Encapsulate the data to be shared as a function/method, and import it into any React components that need to access it. The seems like the simplest approach, but I have the feeling that when writing a ReactJS app everything should be defined as a component, rather than JavaScript classes/functions.
Redux seems to be recommended approach for sharing-state within large apps, but this feels like overkill for smaller projects
Something else?
but I have the feeling that when writing a ReactJS app everything
should be defined as a component, rather than JavaScript
classes/functions.
I really don't see a reason why everything should be a component in React. If it is just data, you can create a single instance of that JS object say and import that anywhere you need it.
I have used similar thing in my app, where I had a "global" kind of object which was saving different configs etc, and then I was using that in the components which needed that data.
Here is also some more info about component communication in React.
A plain-old JavaScript module. Encapsulate the data to be shared as a function/method, and import it into any React components that need
to access it. The seems like the simplest approach, but I have the
feeling that when writing a ReactJS app everything should be defined
as a component, rather than JavaScript classes/functions.
I disagree, React is a library that helps to create user interfaces through components but it doesn't mean that (services, translations, configuration data) have to be built into components, on the other hand, it's actually discouraged you shouldn't couple your services/configuration to a library
you should limit the scope of React to what it is used for. So using plain-old JavaScript modules feels the right way to implement a simple react app.
Redux seems to be recommended approach for sharing-state within large
apps, but this feels like overkill for smaller projects
I think it depends on the complexity of the app rather the size, here is where you should think on, how does your app will evolve or if redux isn't what you really need to remove all this data-sharing dependency within React.
Something else?
The react context (discourage)
The observable pattern
https://www.npmjs.com/package/react-observable-subscribe
I think you should go for the redux solution. It sounds like an over kill but it has an added advantage of having a global state object, therefore you can easily choose when to re-render your app when data is shared across compoents.
you can use context in react :
https://reactjs.org/docs/context.html
or you can create a global variable in window object too
the other way is to use observer design pattern plagin and use it.
mobX or other stateManagement component is good too beside redux