I have a question.
I am making a react-application for a major industry, which contains a huge number of components.
But I have found that most of my components utilised the same functionalities . For eg, I am making use of Material Ui as my UI Component Library.
I have around 10 - 15 MUI-AutoComplete components lying into different sections of the visible browser UI, and I have found that all of these autocomplete components share similar functionalities,
except for "options", onChange, onOpen, onClose, value, and RenderInput's which I could further remove using props, and switch-case logic.
I want to use my components as a single component say, "AppAutoComplete.tsx", which takes care of all of my Autocomplete , that I am using into the different places throughpout my Application, only I have to import this file "AppAutoComplete" into where-ever I want to make use of them, and just tweaking and introducing a new case for each function I receive from the respective component.
I want to know, is this a good approach for centralising components for a major application?
Is this a good way of reducing the similar boilerplate code, from different places into the code base?
Would It create potential dangers, which I might not be aware of this way?
I think this way of writing code would help other developers to keep the track of code written for the app, and also aid in writing the maintainable code.
i mean, all you have to do is tweak/indroduce a new -case for your very new usage of Autocomplete.
the switch includes props.placeName as argument, which I would pass specifially for differnet places where I would Be going to use the Autocomplete
say , If I use autocomplete into different pages, then
**props.PlaceName**
case '`homePage`' : do something specific for home - page's autocomplete
case '`contactPage`' : do something specific for contact- page's autocomplete
case '`servicePage`' : do something specific for service- page's autocomplete, ...ans so on.
Please suggest me what is the better approach?
Should I write the Autocomplete for every-single component, where I am going to make a use of it?
or , should I centralize all the Autocompletes, and target then with switch-case?
Ofcourse creating a separate component for autocomplete is a good idea! you can create a AppAutocomplete.js file that takes properties such as "options, onChange, onOpen, onClose, value" as a prop and passes it into autocomplete component as a props and returns new Autocomplete component.
Related
So I've played a lot with Vue, and now that my app has become large, I am having doubts about how to organize it.
I understand components and that they make sense when you need to re-use them many time on the same page, for example, a "custom select box" component, that will likely be needed in many places.
But what about components that will only have once instance? Example: a administration dashboard interface that has 3 areas: a sidebar with some navigation, a main area with stuff you can edit, based on what is selected in the navigation, another sidebar with stuff related to the main area. Do all these need to be separate components? Because I don't see any benefit of doing that if there is only one instance of each on the page. On the other side, if I stuff all the code in a single "app" component, I could simplify some of the code (less variables)
Summary - typical reasons for using components:
Maintainability.
Rendering performance via component boundaries.
Loading performance via chunking.
If you find that using fewer components improves maintainability then that's fine. It may well be the correct design for your application.
Verbose version below.
The primary reason for using components is to improve maintainability.
Components that are reused in many places such as a select-box are obviously easier to maintain than repeating the same code over and over. However, it is worth considering why that is. It's not just that duplication makes it more difficult to make a change to all of the select-boxes. The other key benefit of using a component is that the extra level of abstraction can reduce mental overhead.
Let's say someone trying to maintain the code sees something like this (pseudo-code):
<select-box :some-prop="blah">
<select-box-option v-for="something" />
</select-box>
Immediately it's clear that this is a select-box. All the gory implementation details of the select-box are hidden away and we don't need to worry about them. By looking at the props, child components and events we can quickly deduce what data goes back-and-forth between the parent component and the select-box. This is just separation of concerns.
This benefit also applies to components that aren't reused. Let's take the sidebar example. We might see this in the template for our main component:
<nav-sidebar #navigate="onNavigate" />
The component's name allows us to quickly identify it as the sidebar. If our current task doesn't involve the sidebar then we can just skip over that bit of the template. As the code has been moved off to a different file we have no difficulty establishing which bits of the code are part of the sidebar and which bits aren't.
In this example the nav-sidebar doesn't have any props and only has a single event. From that we can start to draw some conclusions about how these components interact. It would seem that the nav-sidebar doesn't need anything passed from the main component, it could quite happily live stand-alone. If we need to debug a problem with data flowing the other way we'd almost certainly start with onNavigate.
We couldn't start making deductions like these anything like as quickly if everything was mangled together into one, big component.
Of course it could be that our deductions are wrong. It could be that the nav-sidebar does some horrible things involving $parent to grab data from its parent component. However, that just illustrates why using such techniques is considered bad practice. Maintainable code should allow developers to jump to reasonable conclusions based on the abstractions that appear to be in place.
But it is possible to go too far the other way.
A good abstraction allows you to free up some mental capacity by hiding details behind a label. A poor abstraction adds mental overhead by hiding the code you want to see behind some indirection. Add to that the difficulty of naming things and the burden of extra glue code and you may well be better off just ditching the extra layers and keeping everything inline.
The other thing that can go wrong is splitting components up in the wrong way. Separating concerns requires clean partitions of those concerns. Chop things up slightly differently and you end up with a single concern being spread across multiple components and the resulting mess is typically worse than if you hadn't bothered splitting things up at all.
Vue allows you to split up your JavaScript code in a number of ways, components being just one. Separate .js files, plugins, filters, Vuex, mixins, etc.. There are several options available to you.
Templates, on the other hand, can only really be split up by using components. If you want to break a huge template down into more manageable chunks then components are really the only way to go.
This brings us to another key reason for using components.
A template is compiled down into a render function. When that render function is run it registers reactive dependencies, just like a computed property. If any of those dependencies changes it will trigger a re-render. That runs the whole render function again. Even if that doesn't result in any changes to the DOM it will require the generation of all the relevant VNodes and the diffing algorithm will need to check all of them.
Component boundaries are also rendering boundaries. Each component makes its own decision about whether or not to render based on whether its dependencies have changed.
So, taking the nav-sidebar example, let's say something changes in the nav-sidebar so that it needs a rendering update. If the nav-sidebar is a separate component then it just needs to run the template/render function for that component. If instead we bundle all the nav-sidebar code into the main template then we'll have to re-render everything.
Vue also has support for lazily loaded components as a way to reduce the initial load time of the page. The idea is that many applications have large sections, such as admin interfaces, that aren't relevant to most users. Rather than incurring the overhead of downloading all of those components you can split the components into chunks and download the chunks when they're needed. This is usually implemented via the Vue router configuration.
Chunking aside, the typical way to use the router is to have separate components for the different pages. While in theory it is possible to use the same component for all routes that is unlikely to lead to something more maintainable. I would add that the definition of 'page' is a little fuzzy here but in most applications it's clear what constitutes a different page, resulting in a different component.
No tome on creating code monoliths would be complete without some mention of testing. Unit testing should be thought of as a form of reuse and a particularly extreme form at that. Tests have an unrelenting knack for exposing the mess of spaghetti that hides behind what you thought was a nice, clean design. I'm not going to pontificate on testing but suffice it to say that you won't be able to write unit tests unless you split things into suitable units.
Another key feature of a component is that it has its own set of properties. Its own data and its own computed properties. This sounds obvious but it gains significance when you consider looping via v-for.
<div v-for="item in items">...</div>
The example above uses inline elements instead of components. Any state can only live on the parent. That state needs to be held for each loop item, so we may end up with multiple arrays holding different aspects of the state. Computed properties are similarly difficult to implement when working with loops. We typically end up using methods instead:
<div v-for="item in items" :class="getClassesFor(item)">...</div>
Now consider the component version:
<my-component v-for="item in items" :item="item" />
Each my-component can hold its own state. Let's say, for example, that my-component has expand/collapse functionality. That can now be stored in a local data property within each instance. Likewise each instance will have its own computed properties.
Arguably this is just reuse as the v-for creates multiple instances. However, given we're specifically introducing a new component just to provide a form of property scope, I think it warrants a special mention.
I personally like to have 3 types of components.
Reusable system component. These are used for generic layouts, custom buttons, custom select box ... They are meant to be reused multiple times in the code and be very versatile.
Page/View component. The route usually routes to a specific component. This component is some kind of assembly of multiple components. The distinction allows to quickly identify the "page" of the application.
Logical division. These are the hardest to find. I tend to isolate things that are not related to each other. For instance, a footer may not be reuse but a modification in the footer should concern only the footer. Other examples are the navbar, the menu, each section of an admin. These components should be reusable as much as possible but sometimes they will be specific.
Another example: A comment system. A "comment" would be a component of type 3. A "comment thread" display would be another component of type 3 that use the "comment" component. A page would exist for the subject of a comments thread and would be of type 2.
Note that each component of type 3 and 2 may use other components of other types.
If I want to change the display arrangement of the comment thread I only have to change the "comment thread" component.
Vue components uses not only for re-use. You may split components into logical blocks. SPA for example. You can create grid based on vue components.
Here is the reason why you need to split codes even it is used only once.
With hundreds or thousands of lines of code, it's no question that
splitting code in different files can help make it more manageable.
Although, this can quickly turn into a complicated mess if the
splitting isn't thought out beforehand.
The more you split codes, the clearer it becomes.
It does not only work for Vue JS but also works for almost programming languages.
Say I want to create a little editor for my software that helps me organise people in my company. I will use React & Redux. The editor is a React component (or maybe container?), and it shows me one person at a time. I can edit things about this person, delete fields, etc. and when I am ready I can click a button which will then give me the next person to edit.
Now, I am quite new to React still and can imagine doing this in 2 ways:
Solution 1: Decouple the editor component
Create an Editor Component, which takes an array of all the people in my company. I do have these people saved in the Redux state of my app, but the editor component will not work on my Redux state, but do all changes in its internal state first, and only once I click save will the Editor component commit these changes to my Redux state. This might be a problem, and changes could get lost if somebody doesn't save their edits.
The advantage here is that I have my Editor de-coupled from the rest of my App, and the logic of the Editor will stay in that component.
Solution 2: Connect the Editor Component to Redux
Here, I would connect my Editor component to Redux. So I would not give my component the people array, but direct access to the my Redux store via selectors (for example). I would also not have a deletePerson() function internal to my component, but pass this down as a prop into my component (and it would presumably be a Redux action). This way my component would work directly on state, which I can see as having advantages.
But taking it out of this app and reusing it somewhere else would get more and more difficult, the more complex my component becomes.
Please remember that I am a beginner and that these two solutions are what I came up with as a response to a problem I am facing in my own application. However, I would appreciate if you could help me out here, and explain to me how I should think about problems like this. Is there maybe a third solution I am not mentioning? Or am I misunderstanding a concept?
Personally, I am inclined to go with Solution No. 1, but I also know that the whole idea of Redux is to keep state in one place. What would you advise? Are there performance differences / advantages with one solution?
Your first solution is good but why not use a service when its provided?
You should try to mix up features of Redux React. First, separate your presentational and container components and in one of your container component pass the props directly to redux store on click,etc. Then use redux actions to access the store like deleting,etc. Provide a UI using React presentational components for data entry and pass this data to redux store. Use redux actions when you think they will be useful and not destructure your app.
Go to https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 for further details on how to connect presentational and container components.
When I'm sharing data among components should I call that data only once and provide it as #Input() or should I call that data again on every component's cycle?
For example, I have the following components in one page:
<game-info [id]="params?.id"></game-info>
<game-leaderboard [uid]="auth" [id]="params?.id"></game-leaderboard>
<game-progress [uid]="auth" [id]="params?.id"></game-progress>
Where I get the id from the ActivatedRoute and the uid from my authentication service. In some cases, I'm also passing a data input for multiple components in the same page.
One problem I face is that, sometimes, I'm passing data to many children components and it was harder to debug. For example:
game.component.html
<game-details [data]="data"></game-details>
<game-progress [data]="data"></game-progress>
Then, on details.component.html, I'd pass data as an input to another component - and so on. It became a really long chain:
<game-info [data]="data"></game-info>
<game-players [id]="(data | async)?.$key></game-players>
Is it a proper approach? Or would it be better to get those parameters inside of every component?
Another issue, for example, sometimes I need to get the an async parameter (e.g. uid from an Observable) during onInit but it didn't receive it yet. So, I was wondering if I should just call those parameters straight in the component instead of passing them as an input.
PS. I'm not sure if this is off-topic. If so, please feel free to remove it.
Nothing wrong with that approach. Actually, this is 1 of the recommended ways nowadays where your top-level 'smart' components would gather the data and then inject that data to your 'presentational' aka 'view' aka 'dumb' components. Note that data also flows the other way around: all Events are emitted upwards, where they are handled by the containing 'smart' component. See here for a good (better) explanation.
I must say this approach has been working very well for me: code is clean (clear responsibilities, reusability and testability of presentational components...) although I must admit I share your concern that it might become a bit tedious when you have quite a lot of nested components.
A common approach would be using it as a Injectable service.
For its benefits, as it says:
The component can create the dependency, typically using the new
operator. The component can look up the dependency, by referring to a
global variable. The component can have the dependency passed to it
where it is needed.
For angular 1, check https://docs.angularjs.org/guide/di
For angular 2, check the first answer in What's the best way to inject one service into another in angular 2 (Beta)?
It is hard to answer the question since I am not sure exactly what you are trying to achieve but it might be worth looking into services. Services will have one shared space for the components under the component it is declared under(example the app.component or even app.mudule). Then you can pass the new parameters to other components through the service. That would be the proper way of having a shared state and you can pass through many components by just injecting the service.
I'm creating multi language app. So I'd like to propagate language property to all descendants. Thus I can ad language prefix to all links and routes in my app
I know two ways to do it:
using context, but it is not recommended to use it, because it is experimental.
using redux store but in this case I need to connect every component to store.
So I wish to know which case is more preferable or if none of those, then may be there is another option.
P.S.
3. pass as a property from component to every child all the levels deep, like this:
<Component1 lang={props.lang} />
<Component2 lang={props.lang} />
<Component3 lang={props.lang} />
<Component4 lang={props.lang} />
....
<Component#N lang={props.lang} />
Here are my thoughts and solutions that work the best for me.
Handling internationalization logic is not really the job of the component. Generating links at that level is coupling component too much to the app and makes reuse of them complicated. I have done it in the past and it was a nightmare to maintain. Not even translation related things, think about changing URL for some view when you have generated links to it from different places of your app. I consider it now bad practice and not recommend for anything bigger than TODO app.
Now I'm using separate service which handles dynamically generation of links and routes based on app configuration/internationalization.
That service is exposed as a global in Webpack configuration as let's say LocationPointer. So for example:
LocationPoiner.admin()
will return string pointing to admin view based on an active language (/en/admin-panel).
I don't treat language as a changeable state. For SEO and UX reasons, what I want is internationalization based on a shareable URL, which is the single source of truth.
If you are on SPA and change language by clicking button etc. then the whole app is forced to completely refresh.
During page loading, I detect language, save its value somewhere and pull correct translations.
Note - My backend is using internationalization as well, so when a user changes the language, some requests have to be done again in order to display correct data. It's not just refreshing front-end components. And is worth to remember about it. When your app grows it will probably also respond with different messages/data depending on internationalization.
My pulled front-end translations are stored in another service and exposed as a global through Webpack as __(), which returns translated string.
So if I want Link in my React component I will do:
<Link to={LocationPointer.admin()}>{__('Admin')}</Link>
Passing language as a prop makes sense to me when you use multiple languages on a single page at the same moment. Otherwise, in my opinion - it just creates a mess.
I'm getting started with angular2 and wanted to create a todo-list web app. The UI should consist of two pages (=components) which get slide in/out via JavaScript.
The first one shows all todos in a vertical list and the other one shows additional details when a todo item from the list is clicked.
I'm now asking myself, what's the right way in angular2 to declare the page components?
Should I build a generic component like this?
<page type="list"></page>
<page type="detail"></page>
Or should I create a new component for each page?
<listpage></listpage>
<detailpage></detailpage>
In general, without knowing more details, my gut sense would be that the latter would be more appropriate, i.e. create a new component for each page.
You require two fundamentally distinct types of entities:
a collection and
a single item from that collection.
The first solution that you propose, i.e. a generic component, would be more suitable to multiple entities that all share some basic underlying structure but differ in some (but not all) details, e.g., two different collection views that both list all items but format those items in two different ways. To force that strategy onto your use case would require your generic page component to have no universally shared structure: what template or logic would you share between a todo-list collection and a single todo-item from that collection? Everything would depend on the value of the type attribute. Then what meaning would page have? Essentially nothing.
Your latter suggestion, i.e. having two distinct components for these two different entities, seems more in the true spirit of how angular components are meant to be used.
I suppose one could argue that a more generic page component/view could have some valuable structure that persists for both the list view and the detail view, e.g. main title, navigation links, user info, etc. However, even if you implemented that, I think you'd want to eventually create separate (more deeply nested?) components for the whole list versus for an individual item, which eventually comes back around to implementing your latter suggestion.
I think a useful model is the example code provided on the official Angular2 web site. I could point to several different examples, but I think the clearest parallel to your situation is in the "Advanced Documentation" section, under the "Routing & Navigation" heading. There they provide code that separates components as follows:
hero-list.component: This would seem to parallel your listpage.
hero-detail.component: This would seem to parallel your detailpage.
Clearly they've separated out these two parts of the app into distinct components.
This sort of strategy decision may also depend on the size/complexity of your "entities". If your "list" and "detail" views were both extremely simple, I suppose you could distinguish between them within a single component (e.g. page), just using an attribute (e.g. type). However, in a todo app, I can't imagine either a list view or a detail view being extremely simple. Thus trying to squash both into a single page component would make the component too complex.