I've observed a way we import and extending in react. The way I usually write is as following.
import React from 'react';
class SomeClass extends React.Component {
//Some code
}
but I've seen people using the following way as well.
import React, { Component } from 'react'
class SomeClass extends Component {
//Some code
}
What is the best way or the best practice when writing the imports. is it the 1st way or the 2nd way? or is there any other way as the best practice. Please advice.
There is no best practice for this particular case. Both snippets do exactly the same thing. As #Rajesh said, it's only a matter of opinion and personal preference.
Personally, I prefer approach 1 because we have already imported React which is where Component is under. Also, I think extends React.Component vs extends Component makes little difference to the eye.
That being said, if you are exporting multiple React components from the same file, it makes more sense to use approach 2, as that would shorten your code a bit.
One example where case two is useful is say you have a module that exports multiple hard-coded constants or helper functions
Such as
types.js:
export const ACTION_1 = "...";
export const ACTION_2 = "...";
From an understanding point of view, it's pleasing to a developer to see in other file:
import {
ACTION_1,
ACTION_2
} from './types.js'
At the end of the day, it's syntactic preference.
Related
I have a typical Glimmer "base" component:
import Component from '#glimmer/component';
export default class BaseComponent extends Component { ... }
It has a template like normally, but the actual implementations of that component are child componenents, that override some of the template getters and parameters so that it works with various different data types.
export default class TypeAComponent extends BaseComponent { ... }
export default class TypeBComponent extends BaseComponent { ... }
etc.
My question is: How do I specify that all the child components should use the parent class template, so I don't have to duplicate the same fairly complex HTML for all child components? Visually the components are supposed to look identical so any changes would have to be replicated across all child component types. Therefore multiple duplicated templates isn't ideal.
In Ember Classic components there was layout and layoutName properties so I could just do:
layoutName: 'components/component-name'
in the base component and all child components did automatically use the defined template.
Now that I'm migrating to Glimmer components I can't seem to figure out how to do this. I have tried:
layout property
layoutName property
template property
Using the child components without a template in hope that they would automatically fall back to the parent class template.
Only thing that seems to work is creating Application Initializer like this:
app.register('template:components/child1-component', app.lookup('template:components/base-component'));
app.register('template:components/child2-component', app.lookup('template:components/base-component'));
But that feels so hacky that I decided to ask here first if there is a proper way to do this that I have missed?
How to specify template for Glimmer Component?
tl;dr: you should avoid this.
There are two answers to two, more specific, questions:
What is the recommended way to manage complex components with shared behaviors?
Typically, you'll want to re-work your code to use either composition or a service.
Composition
<BaseBehaviors as |myAPI|>
<TypeAComponent #foo={{myAPI.foo}} #bar={{myAPI.bar}} />
<BaseBehaviors>
Where BaseBehaviors' template is:
{{yield (hash
foo=whateverThisDoes
bar=whateverThisBarDoes
)}}
Service
export default class TypeAComponent extends Component {
#service base;
}
and the service can be created with
ember g service base
then, instead of accessing everything on this, you'd access everything on this.base
Ignoring all advice, how do I technically do the thing?
Co-located components (js + hbs as separate files), are combined into one file at build time, which works like this:
// app/components/my-component.js
import Component from '#glimmer/component';
export default class MyComponent extends Cmoponent {
// ..
}
{{! app/components/my-component.hbs }}
<div>{{yield}}</div>
The above js and hbs file becomes the following single file:
// app/components/my-component.js
import Component from '#glimmer/component';
import { hbs } from 'ember-cli-htmlbars';
import { setComponentTemplate } from '#ember/component';
export default class MyComponent extends Cmoponent {
// ..
}
setComponentTemplate(hbs`{{! app/components/my-component.hbs }}
<div>{{yield}}</div>
`, MyComponent);
So this means you can use setComponentTemplate anywhere at the module level, to assign a template to a backing class.
Why is this not recommended over the other approaches?
All of this is a main reason the layout and related properties did not make it in to Octane.
Formally supported Component inheritance results in people getting "clever"
this in of itself, isn't so much of a problem, as it is what people can do with the tool. Bad inheritance is the main reason folks don't like classes at all -- and why functional programming has been on the rise -- which is warranted! Definitely a bit of an over-correction, as the best code uses both FP and OP, when appropriate, and doesn't get dogmatic about this stuff.
Component Inheritance is harder to debug
Things that are a "Foo" but are a subclass of "Foo" may not actually work like "Foo", because in JS, there aren't strict rules around inheritance, so you can override getters, methods, etc, and have them provide entirely different behavior.
This confuses someone who is looking to debug your code.
Additionally, as someone is trying to do that debugging, they'll need to have more files open to try to under stand the bigger picture, which increases cognitive load.
Component inheritance allows folks to ignore boundaries
This makes unit testing harder -- components are only tested as "black boxes" / something you can't see in to -- you test the inputs and outputs, and nothing in between.
If you do want to test the in-between, you need to extract either regular functions or a service (or more rendering tests on the specific things).
I would say this is the classic case for a composition, where TypeAComponent and TypeBComponent use the BaseComponent.
So you have your BaseComponent with all the HTML, that basically is your template. I think its important here to think a bit more of Components also as possible Templates, not only full Components. So lets call this the TemplateComponent.
So you have your TemplateComponent which could also be a template-only component. Then you have as template for TypeAComponent and TypeBComponent:
<TemplateComponent
#type={{#title}}
#title={{#title}}
#onchange={{#onchange}}
#propertyThatIsChanged={{this.propertyThatIsChanged}}
...
/>
this allows you to have a getter propertyThatIsChanged to overwrite pieces. Common behaviour can also be placed on the TemplateComponent, or, if its common code, maybe on a BaseCodeComponent, that only contains shared code, while I would rather not do this.
For areas you want to replace this also opens the possibility to use Blocks.
The TemplateComponent, for example, could use has-block to check if a :title exists, use this block then ({{yield to="default"}}), and if not just use {{#title}}.
So, to the only obvious downside of this: you have to proxy all params. This seems ugly at first, but generally I think its better for components not to have too many arguments. At some point an options or data argument could be better, also because it can be built with js when necessary. It should also be mentioned that there is an open RFC that would address this issue. With the upcoming SFCs, I think this is the much more future-proof solution overall.
I've seen somewhere in github people import *, I have no clue why he did so, for instance:
import * as React from 'react';
export const doSomething = () => {
return React.useMemo(() => {
return {
something: window.location.href
};
}, []);
};
He did the same for other packages. Why don't we do
import { useMemo } from 'react' ?
Both of the suggestions for using * or explicit imports are possible and will work as expected.
The asterix will import all components, hooks, and more artifacts that are located under the "react" library, so when using
import * from "react";
you'll have the freedom to use any type of hook or Component without an explicit import.
The best practice though is to import only modules that are relevant to your application or components
import React, { useMemo } from "react";
This approach will make sure you don't import unused components that will be heavy on your bundle size for production and will be flagged as dead code.
Ok, to expand my answer I will say: You have two options
import React, { useMemo } from 'react';
Benefits? You are just importing the things you need, also, your code will be a little bit shorter compared to the second option since you can use useMemo(); directly
Also this is the way reactjs.org presents their examples https://reactjs.org/docs/hooks-intro.html which is a big -GOOD PRACTICE- signal
import React from 'react';
it's seem similar to the first option but you are using React directly.
how to access useMemo()? React.useMemo(). So yes, you guessed, it's a little bit longer sintax that the first option, but i do not think this is a problem.
Also this is the way https://material-ui.com/components/text-fields/ material ui presents their examples. It's also a nice way to do it (and the one I use)
I was working with some coworkers on a React component, and I had imported React and started to write my class as
import React from 'react'
Class MyComponent extends React.Component
when they suggested I import Component as well, i.e.:
import React, { Component } from 'react'
This is a new pattern to me, and my first reaction was to guess that importing both the namespace and a child module would cause them to be double-imported, as we'd have both a React object and a Component object (which also exists under React).
My second reaction was to think that since objects are passed by reference, both React.Component and Component would reference the same object in memory, so the only real difference is that there'd be an extra (but minimal -- not counting the other modules under React) React object.
However, I'm not sure. Which is correct? Or is neither?
No, the module won't be double-imported. There isn't much difference really between this two ways of importing. The second way makes sense if you have many components in a file, so you can write just extends Component instead of extends React.Component.
In this case,
import React, { Component } from 'react';
is essentially the same as
import React from 'react';
const { Component } = React;
If I have an ES6 component like so:
component OldThing extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.someProp}</div>;
}
}
an ES6 container like so:
const OldThingContainer = connect(mapStateToProps, mapDispatchToProps)(OldThing);
and a new typescript component like so:
component NewThing extends React.Component<Props, State> {
render() {
return <OldThingContainer someProp={someValue} />
}
}
I currently get an IntrinsicAttributes error about someProp not existing on OldThing. How do I get rid of that error without adding types/typedefs to/for OldThing and/or OldThingContainer? I tried the #augments solution on the OldThingContainer where it's defined but that doesn't seem to help. Neither did adding the comment on the component itself.
The reason I want to be able to import JS without types is that we are adding typescript to a very large, multiple years old code base and it's currently difficult to get people to write typescript components at all, let alone if they also have to type out every existing component their component wants to import.
Is there an existing elegant solution to this problem or am I going to have to go through ahead of everyone and manually type every existing JS component (hundreds) in some typedef file to get rid of/prevent these errors?
I tried your example and it seemed some issue with the type connect inferred for your component.
One quick'n'dirty fix would be to annotate your JS component with:
/**
* #type {React.ComponentType<any>}
*/
const OldThingContainer = connect(mapStateToProps, mapDispatchToProps)(
OldThing
);
One more elegant fix (but maybe not possible) would be trying to dive into the react-redux typings. The connect function requires you to supply some type arguments for it to properly work. When not supplied, those arguments are given {}, not any (because any can silently eat your types).
Comment: From the few I've worked with react+redux+typescript, lib typedefs are one of the biggest pain points. React typedefs can get really complicated. (flow just will hang up your editor with "waiting for flow server", which is even worse). As TS+Redux gets more popular and more support is added, this may get a lot better in a few months...
What is the major benefit of writing
import React, { Component } from 'react';
class Link extends Component {
...
}
instead of
import React from 'react';
class Link extends React.Component {
...
}
when it comes to react 15.4.x??
In my perspective and in my case (correct me if I am wrong) it does not matter at all since:
I am using a webpack2 for making my bundles;
I use code splitting to split my app code from vendor code;
I use webpack.optimize.CommonsChunkPlugin plugin with minChunks: Infinity setting to make sure that all vendor code is included only once.
From understanding how ES6 imports work I understand that by using named import of {Component} I state that I want to use only Component component in my code, which looks.. cleaner.
But since whole React package is still used in the app, I can create my classes with extension from React.Component instead of just Component and in result webpack will still produce the same amount of code and my bundle size will be the same in both cases.
Am I correct?
There is no difference, React.Component is the same object as Component, the second way is more eloquent in my opinion, because it really explains that you are using the Component object from the React library.
The first one seems to refer a member,
but, it comes from the pre modules era of javascript, where everything had to be attached to the exported global namespace (just to avoid global namespace pollution).
something that could be under the hood:
// this should be assumed as an example only.
class React { ... }
class Component { ... }
React.Component = Component;
// ES6
export {Component}
export default React;
// ES5
window.React = React;
Note: as someone said, you also need to import React because JSX needs to have it on scope, but, if you want to avoid it, you can expose React globally (window.React = React)
This import statement:
import React, { Component } from 'react';
is really doing two things. It imports the default export, under the name React (which is just a convention, you could call it what you want). It also imports a named export, Component.
The reason that the default React is imported is actually to make JSX work. When your JSX code is transpiled, then it substitutes <div> for React.DOM.div(), so React must exist otherwise things break!
Importing both things separately means that your JSX works but you get to write Component instead of React.Component in your code.
When you do import anything from "react", then the whole file is going to get included either way - any attempt to reduce the bundle size (e.g. Dead Code Elimination, Tree Shaking) is an additional, separate step, which doesn't depend on your import statements but the parts of the code that you use.
In the case of this library, the sane thing happens: the child Component of the default export refers to the same thing as the named export Component.
However, bear in mind that this isn't guaranteed to be the case! If the React library code contained the following:
export default {
Component: "foo"
};
export const Component = "bar";
Then React.Component === "foo" and Component === "bar".