I made a custom hook that only returns helper functions. Nowhere in the custom hook do I use another hook (useState, useEffect...)
Example:
import backend from '../lib/backend';
import axios, { AxiosError } from 'axios';
interface PresignedData {
fields: any;
url: string;
key: string;
}
type Resource = 'users' | 'events';
function useBucket() {
const uploadViaPresignedPost = async function (
resource: Resource,
file: File
) {
...
};
const buildImageUrl = function (key: string) {
return `${process.env.NEXT_PUBLIC_S3_BUCKET_DOMAIN}/${key}`;
};
return { uploadViaPresignedPost, buildImageUrl };
}
export default useBucket;
Is this common practice? Or would it be better to create a class with static methods? export helper functions from separate file? What is best practice?
Using a class for this wouldn't make all that much sense because a class is meant for when you need to tie together data associated with an instance with methods that can operate on that data. If there's no data associated with an instance - if the class never has new called on it - then there's not much point to a class in the first place. A few plain functions or a plain object with functions in it would make more sense than a class.
Your current approach of a custom hook that doesn't use any other hooks inside it seems a bit weird, but it's not forbidden. Feel free to use that approach if you want. Using a custom hook has an added benefit that if you later decide to change the logic and, for example, feel the desire to add a useState or useEffect or something to the custom hook, it's trivial to add them into the custom hook. In contrast, if you used anything other than a custom hook and later found that you needed to add something that required hook logic, you would not be able to without first refactoring everything back into a custom hook again.
Related
I have a constants file constants.ts:
export const C0NST = "constant";
I access it in a service some.service.ts like so:
import { C0NST } from './constants';
console.log(C0NST); // "constant"
However, when I access it in a component template:
some.component.ts:
import { C0NST } from './constants';
some.component.html:
{{ C0NST }} <!-- Outputs nothing -->
However defining a member in the component class works:
some.component.ts
public const constant = C0NST;
some.component.html
{{ constant }} <!-- constant -->
I don't understand why I was able to access the imported constant directly in the service class but not in the component template even though I imported it in the component class.
In Angular2, the template can only access fields and methods of the component class. Everything else is off-limits. This includes things which are visible to the component class.
The way to go around this is to have a field inside the component, which just references the constant, and use that instead.
It's one limitation of the design, but perhaps you should think a bit more about why you need a constant in the template in the first place. Usually these things are used by components themselves, or services, but not the template.
Since in the Component's template you can only use attributes of the Component's class, you can't directly use any external constants (or external variables).
The most elegant way that I've found so far is the following:
import { MY_CONSTANT } from '../constants';
#Component({
// ...
})
export class MyTestComponent implements OnInit {
readonly MY_CONSTANT = MY_CONSTANT;
// ...
}
which basically just creates a new attribute MY_CONSTANT inside the component class. Using readonly we make sure that the new attribute cannot be modified.
Doing so, in your template you can now use:
{{ MY_CONSTANT }}
The scope of Angular2 template bindings is the component instance. Only what's accessible there can be used in bindings.
You can make it available like
class MyComponent {
myConst = CONST;
}
{{myConst}}
There are two best directions in my opinion:
Wrapping constants as internal component property
enum.ts
export enum stateEnum {
'DOING' = 0,
'DONE',
'FAILED'
}
component.ts
import { stateEnum } from './enum'
export class EnumUserClass {
readonly stateEnum : typeof stateEnum = stateEnum ;
}
Example uses enum, but this can be any type of defined constant. typeof operator gives you all of benefits of TypeScript typing features. You can use then this variable directly in templates:
component.html
<p>{{stateEnum.DOING}}<p>
This solution is less efficient in memory usage context, because you are basically duplicating data (or references to constants) in each component you wish to use it. Beside that, syntax
readonly constData: typeof constData = constData
in my opinion introduce a lot of syntax noise and may be confusing to newcommers
Wrapping external constant in component function
Second option is to wrap your external variable/constant with component function and use that function on template:
enum.ts
export enum stateEnum {
'DOING' = 0,
'DONE',
'FAILED'
}
component.ts
import { stateEnum } from './enum'
export class EnumUserClass {
getEnumString(idx) {
return stateEnum[stateEnum[idx]];
}
}
component.html
<p>{{getEnumString(1)}}</p>
Good thing is that data is not duplicated in controller but other major downside occur. According to Angular team, usage of functions in templates is not recommended due to change detection mechanism, which works way less efficient in case of functions returning values to templates: change detection have no idea does value return by a function has changed, so it will be called way often than needed (and assuming you returning const from it, it's actually needed only once, when populating template view. It may be just a bit efficiency killing to your application (if you are lucky) or it may totally break it down if function resolves with Observable for instance, and you use async pipe to subscribe to results. You can refer to my short article on that HERE
You can create a BaseComponent , it is a place where you should create your constant instances and then you can create your FooComponent extends BaseComponent and you can use your constants.
I'm converting a react project from redux to mobx, and I'm having the following problem: I was using the container/presenter pattern with redux, which meant using the redux "connect" function, like this:
export default connect(mapStateToProps, mapDispatchToProps)(Leads);
The problem I'm having is that there's no equivalent mobx function, so instead, I tried to simply create an instance of the component in the container. Something like:
render() {
return (
<MyComponent
store={mystore}
/>
);
}
Unfortunately, that doesn't work, because MyComponent has injected properties from react-router, something like this:
class MyComponent extends React.Component<ReportProps & RouteComponentProps<ReportProps>> {
constructor(public routeProps: ReportProps & RouteComponentProps<ReportProps>) {
super(routeProps);
}...
I tried getting rid of the container concept, but the same problem occurs in other places because I'm using the mobx-react #inject decorator. For example, I have a component like this:
export interface AddressProps {
report: IReportStore;
}
#inject((rootStore: RootStore) => ({
report: rootStore.report
}))
#observer
class Address extends React.Component<AddressProps> {
...
If I then try to use my component somewhere, typescript complains that I'm not passing the required property (report, in this instance), even though I shouldn't need to, since I'm injecting the properties.
I figure I must be missing something basic, as this is a fairly straightforward use of mobx. Or maybe it's just a typescript problem...? If so, any ideas how to fix or work around it?
Thanks in advance,
Jonathan
There are a lot of problems around the mobx inject method.
the original idea was to return a Partial<TProps> but you can't do this typed without losing your original Class: React.Component<Partial<TProps>,TState> != YourComponent - the set properties.
Read the discussed problem here: https://github.com/mobxjs/mobx-react/issues/256
Simplistic solution
Use optional parameters in props and set them in a getter property:
interface AppProps {
store?: SDtore;
}
class App {
get store(): TimeEntryStore {
return this.props.store as Store;
}
method(){
this.store;///
}
}
Other solution
If you want to keep your required parameters (eg: for using the component outside of mobx etc).
You can consider casting the component to React.Component<TPropsReduced,State> where TPropsReduced is a self defined interface with required props after injected.
The downsides are:
Losing type safety at casting (eg if you make mistakes/typo's in the interface properties. (can be solved by extending/subclassing interfaces.
Losing methods calls. You will no longer have typed methods on the component (eg: when you use ref={()=>}), but using this is dis-advised anyways.
Example:
interface AddressPropsMobx {
}
interface AddressProps extends AddressPropsMobx {
report: IReportStore;
}
//Pure react component
class Address extends React.Component<AddressProps> {}
// Mobx version:
const MobxAddress = inject((rootStore: RootStore) => ({
report: rootStore.report
}))(observer(Address)) as React.Component<AddressPropsMobx>; //unsafe cast
I'm trying to figure out how to get a reference to a class in Angular 2+ (Angular 5) from a string at runtime. I tried the examples on this page. This one didn't work for me:
console.log((<any>Object).create(window[className])); // undefined
And the others are using an import, which I'm trying to avoid.
I'm not using a namespace, but don't know if Angular has one of its own. I tried snooping on the window object to see if I could find anything. All I found were getAllAngularRootElements, getAllAngularTestabilities, and getAngularTestability, but those didn't seem like what I was looking for.
I had a similar need once for dynamically rendering components and only having a string reference to the class that needed to be injected into the page (dynamic dashboard type of app). I ended up doing the following:
Create service to hold onto reference of component by string name
Inject service into module component was part of and register the component
Inject the service into the component that needed to get the component by string name
This was roughly what I had for the service (the class took care of actually creating the dynamic component instead of getting the reference like below):
export class DynamicComponentService {
private dynamicComponentTypes: { [type: string]: Type<BaseInterfaceSectionComponent> } = {};
registerDynamicComponentTypes(...dynamicComponentTypesToRegister: { component: Type<BaseInterfaceSectionComponent>, name: string }[]) {
dynamicComponentTypesToRegister.forEach(dynamicComponentType => {
this.dynamicComponentTypes[dynamicComponentType.name] = dynamicComponentType.component;
});
}
getDynamicComponentType(name: string): Type<BaseInterfaceSectionComponent> {
return this.dynamicComponentTypes[name];
}
}
I was unaware until doing this, but you can actually inject dependencies into a module's constructor. I used this feature to use the service to register the dynamic components:
export class BarChartContentModule {
constructor(dynamicComponentService: DynamicComponentService) {
const dynamicComponent = { component: BarChartContentComponent, name: 'BarChartContentComponent' };
dynamicComponentService.registerDynamicComponentTypes(dynamicComponent);
}
}
Not sure if this is what you were looking for, but figured I'd share.
I'm looking at some ES6 code and I don't understand what the # symbol does when it is placed in front of a variable. The closest thing I could find has something to do with private fields?
Code I was looking at from the redux library:
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'redux/react';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/CounterActions';
#connect(state => ({
counter: state.counter
}))
export default class CounterApp extends Component {
render() {
const { counter, dispatch } = this.props;
return (
<Counter counter={counter}
{...bindActionCreators(CounterActions, dispatch)} />
);
}
}
Here is a blog post I found on the topic: https://github.com/zenparsing/es-private-fields
In this blog post all the examples are in the context of a class - what does it mean when the symbol is used within a module?
I found the accepted answer was not enough to help me sort this out, so I'm adding a little more detail to help others who find this.
The problem is that it's unclear exactly what is the decorator. The decorator in the example given is not just the # symbol, it's the #connect function. Simply put, the #connect function is decorating the CounterApp class.
And what is it doing in this case? It's connecting the state.counter value to the props of the class. Remember that in redux the connect function takes two arguments: mapStateToProps and mapDispatchToProps. In this example, it's taking only one argument - mapStateToProps.
I haven't investigated this too much, but this appears to be a way to encapsulate your state-to-props and dispatch-to-props mappings so they accompany your components rather than being located in a different file.
It's a decorator. It's a proposal to be added to ECMAScript. There are multiple ES6 and ES5 equivalent examples on: javascript-decorators.
Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
They are commonly used to control access, registration, annotation.
What is #myDecorator()?
The # symbol in javascript stands for a decorator. Decorators are not present in ES6 so the in code you are working with the decorator is probably transpiled to an version of javascript which can be run in any browser.
What is a decorator?
A decorator extends (i.e. decorates) an object’s behavior dynamically. The ability to add new behavior at runtime is accomplished by a Decorator object which ‘wraps itself’ around the original object. A decorator is not just a concept in javascript. It is a design pattern used in all object oriented programming languages. Here is a definition from wikipedia:
In object-oriented programming, the decorator pattern is a design
pattern that allows behavior to be added to an individual object,
dynamically, without affecting the behavior of other objects from the
same class. The decorator pattern is often useful for adhering to the
Single Responsibility Principle, as it allows functionality to be
divided between classes with unique areas of concern
Why use a decorator?
The functionality of an object can be modified at runtime when using a decorator. For example, in your code you simply imported the decorator and added it to your CounterApp class. Now your CounterApp has dynamically added functionality Without you knowing the implementation details.
Example:
// decorator lights is a function which receives the class as an argument
let lights = function(tree) {
// The behaviour of the class is modified here
tree.treeLights = 'Christmas lights'
}
#lights // the decorator is applied here
class ChristmasTree {}
console.log(ChristmasTree.treeLights); // logs Christmas lights
say I have a component connected to the redux store. Within this store, there's a list of objects. For instance something like this:
ReduxStore: {
dataList: [
{name:'bla'},
{name:'blub'},
]
}
Is it actually an anti pattern to create and add a filter function within the reducer to create something like this:
ReduxStore: {
dataList: {
data:[
{name:'bla'},
{name:'blub'}
],
isNameAvailable: (name) => {/* search for name */}
}
}
It works great, but I'm not sure whether this was the intended way to go.
It's an anti-pattern because your store only cares about data, not computation. Once you start adding functions to your store, you lose the ability to serialize the data inside it.
However, it's pretty trivial to pull these kinds of helper functions out and turn them into standalone utility selectors which you can use to achieve the same thing.
function isNameAvailable(store, name) {
/* search for `name` in `store` */
}
Now the function works independently of the store and you can keep them separate for testing.
From here, you can take a look at Reselect which allows you to turn your isNameAvailable function into a cached selector, meaning you'll only need to re-calculate as and when the appropriate data in the store is changed.
From Redux documentation:
It’s very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
so I think if you want to declare a filter function isNameAvailable, in my opinion, you have two options:
use the connect function to filter
// the solution of connect
import React, { Component } from 'react';
import { connect } from 'react-redux';
const App = () => (
<div>the example</div>
);
function isNameAvailable(state) {
// your code
}
function mapStateToProps(state) {
return isNameAvailable(state)
}
module.exports = connect(mapStateToProps)(App);
declare a filter function in action
In my experience,I think the first one is better. I hope this can help you.