I make my own react boilerplate and come until updating packages. So I've update react across major version (v15x to v16x), and change my code on a module file called injectReducer. So what i've changed in injectReducer is from this:
static contextTypes = {
store: PropTypes.object.isRequired,
};
to this:
static contextType = ReactReduxContext;
Now I think this doesn't do any breaking changes on my boilerplate, because the use of the module still the same (backward compatible). But what about the package I've updated? Should any library updated across major is considered breaking changes? What about minor updates?
Here is my full code of injectReducer:
import React from 'react';
import { ReactReduxContext } from 'react-redux';
import hoistNonReactStatics from 'hoist-non-react-statics';
import getInjectors from './reducerInjectors';
export default ({ key, reducer }) => (WrappedComponent) => {
class ReducerInjector extends React.Component {
static WrappedComponent = WrappedComponent;
static displayName = `withReducer(${(WrappedComponent.displayName || WrappedComponent.name || 'Component')})`;
static contextType = ReactReduxContext;
injectors = getInjectors(this.context.store);
componentWillMount() {
const { injectReducer } = this.injectors;
injectReducer(key, reducer);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return hoistNonReactStatics(ReducerInjector, WrappedComponent);
};
But what about the package I've updated? Should any library updated across major is considered breaking changes?
That is the idea behing the major version in semver
when you make incompatible API changes
Except I would consider it does not involve just the API.
See as an example "Semantic Versioning, Go Modules, and Databases" (just to illustrate the kind of non-API change which would still warrant a major version bump)
Basically, if any client of your library has work to do in order to adapt to the new version... your change should be considered major.
If not: minor.
Related
Below is my code:
import * as React from 'react';
import styles from './PnpfolderoperationsDemo.module.scss';
import { IPnpfolderoperationsDemoProps } from './IPnpfolderoperationsDemoProps';
import { escape } from '#microsoft/sp-lodash-subset';
import { PrimaryButton } from 'office-ui-fabric-react';
import { sp } from "#pnp/sp/presets/all";
export default class PnpfolderoperationsDemo extends React.Component<IPnpfolderoperationsDemoProps, {}> {
constructor(props){
super(props);
sp.setup({
spfxContext: this.context
});
}
public render(): React.ReactElement<IPnpfolderoperationsDemoProps> {
const {
description,
isDarkTheme,
environmentMessage,
hasTeamsContext,
userDisplayName
} = this.props;
Executed below command for packages
npm install #pnp/logging #pnp/common #pnp/odata #pnp/sp --save
Getting following error with pnpjs:
#Module '"#pnp/sp/presets/all"' has no exported member 'sp'.
Has anyone faced any similar situation?
Thanks
You may be using pnpjs v3.x Your code seems to be valid for v2.x, in v3.x there are changes (global "sp" object has been deprecated). Please check the transition guide, or use v2.x
https://pnp.github.io/pnpjs/transition-guide/
To install the previous version (v2.x) that is known to be compatible with the SPFx examples you seem to be using, try specifying the version explicitly:
npm install #pnp/logging#2.11.0 #pnp/common#2.11.0 #pnp/odata#2.11.0 #pnp/sp#2.11.0 --save
The Problem
I am importing a class in two related projects. It appears that the prototype chain of a class instance created in one project does not match the version of the class in the other project.
Specifically, instanceof returns false.
The Code
I have three npm packages / projects (names invented for simplicity):
classes (contains Payload)
processors (contains ProcessPayload); depends on classes
main (not actually a package); depends on classes and processors
The main project creates a Payload and passes it to a ProcessPayload object.
To be clear: these projects have three separate package.json and any associations between them are done via dependencies.
main/index.js
import { Payload } from 'classes'
import { PayloadProcessor } from 'processors'
const payload = new Payload()
const processor = new PayloadProcessor()
console.log(payload instanceof Payload) // true
processor.process(payload)
The ProcessPayload object imports Payload and uses it to validate the type of its parameters.
processors/PayloadProcessor.js
import { Payload } from 'classes'
class PayloadProcessor {
process = (obj) => {
console.log(obj instanceof Payload) // false
}
}
export default PayloadProcessor
The Question
What is going on with the prototype chains here. I plan to use duck typing and create an isPayload method to solve my problem, but are there really two versions of Payload floating around in my dependency stack? Is there any way to get instanceof to work as expected in this situation?
I have a newbie question on TypeScript imports. I tried to make a class which holds some data in a static variable, and the data is lazily initialised in getInstance() method.
myStaticClass.ts:
class MyData {
x = 1;
}
export class MyStaticClass {
private static data: MyData;
static getInstance() {
if (MyStaticClass.data == null) {
console.log('data is null, initialising');
MyStaticClass.data = new MyData();
}
return MyStaticClass.data;
}
}
I imported this class in 2 other classes:
a.ts
import { MyStaticClass } from './MyStaticClass';
// NOTE the typo above - uppercase file name
export class A {
logX() {
console.log(MyStaticClass.getInstance().x);
}
}
index.ts
import { MyStaticClass } from './myStaticClass';
import { A } from './a';
console.log(MyStaticClass.getInstance().x);
new A().logX();
To my surprise, the output of ts-node index.ts is
data is null, initialising
1
data is null, initialising
1
If I correct the import the output is as expected - data is initialised only once.
I also checked that I get one initialisation for one variant of spelling (added 3rd class with another letter in upperCase)
Can anyone explain why this behaviour is in place?
(Additionally, what tools / debug statements I could have used to identify what is happening?)
Can I force TypeScript to flag this as error?
I am on MacOs, TS 3.6.3, node-ts 8.4.1
While on Windows two differently cased file names always point to the same file, on other platforms they do not. This means that imports with different file names are treated as different modules, this is by design and is not considered an issue (see here for node discussion)
The simple solution is to force consistent file casing to be used when importing modules. Typescript does have a compiler option to force this named forceConsistentCasingInFileNames (See docs). This option should prevent such issues.
I'm looking for a way to get translation text without using FormattedMessage. Until now, I've found only this solution that provides to use ContextTypes an React's EXPERIMENTAL feature. Are there others ways to accomplish this (or other library/npm module)?
I prefer using context, but react-intl does also provide a higher order component injectIntl you can use instead. This will pass a prop intl that has all the imperative formatting functions.
import React from "react";
import {injectIntl, intlShape} from "react-intl";
class MyComponent extends React.Component {
static propTypes = {
intl: intlShape.isRequired
}
render() {
return <p>{this.props.intl.formatDate(new Date())}</p>;
}
}
export default injectIntl(Component);
I am wondering how would you use typescript IOC specifically node app.
In case of external module-based architecture there is no any classes in the app. Just pure modules because my app heavily depends on node_modules.
How would I integrate IOC solution in such case? Any thoughts?
Here is my specific case I want to use IOC for:
I have mongoose model:
interface IStuffModel extends IStuff, mongoose.Document { }
var Stuff= mongoose.model<IStuffModel>('Stuff', Schemas.stuffSchema);
export = Stuff;
And related fake class:
export class Stuff implements IStuff {
//do stuff
}
How would I integrate IOC solution in such case
Here is a very popular library that I recommend : https://github.com/inversify/InversifyJS
External modules
Using external modules doesn't change the code at all. Instead of
kernel.bind(new TypeBinding<FooBarInterface>("FooBarInterface", FooBar));
Production
You just have
import {ProdFooBar} from "./prodFooBar";
kernel.bind(new TypeBinding<FooBarInterface>("FooBarInterface", ProdFooBar));
Test
import {MockFooBar} from "./mockFooBar";
kernel.bind(new TypeBinding<FooBarInterface>("FooBarInterface", MockFooBar));
As Basarat indicated in his answer, I have developed an IoC container called InversifyJS with advanced dependency injection features like contextual bindings.
You need to follow 3 basic steps to use it:
1. Add annotations
The annotation API is based on Angular 2.0:
import { injectable, inject } from "inversify";
#injectable()
class Katana implements IKatana {
public hit() {
return "cut!";
}
}
#injectable()
class Shuriken implements IShuriken {
public throw() {
return "hit!";
}
}
#injectable()
class Ninja implements INinja {
private _katana: IKatana;
private _shuriken: IShuriken;
public constructor(
#inject("IKatana") katana: IKatana,
#inject("IShuriken") shuriken: IShuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };
}
2. Declare bindings
The binding API is based on Ninject:
import { Kernel } from "inversify";
import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";
var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);
export default kernel;
3. Resolve dependencies
The resolution API is based on Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
The latest release (2.0.0) supports many use cases:
Kernel modules
Kernel middleware
Use classes, string literals or Symbols as dependency identifiers
Injection of constant values
Injection of class constructors
Injection of factories
Auto factory
Injection of providers (async factory)
Activation handlers (used to inject proxies)
Multi injections
Tagged bindings
Custom tag decorators
Named bindings
Contextual bindings
Friendly exceptions (e.g. Circular dependencies)
You can learn more about it at https://github.com/inversify/InversifyJS
In the particular context of Node.js there is a hapi.js example that uses InversifyJS.