Importing a single rxjs operator not working with react and webpack - rxjs

I am using redux-observable for react app and webpack for bundling.
When I include a specific operator from rxjs like
import 'rxjs/add/operator/mapTo';
it doesn't work and throws error
TypeError: action$.ofType(...).mapTo is not a function.
But when I include complete rxjs library, it works
import 'rxjs';
When importing specific operator, my js bundle does contain mapTo code but the methods are not getting included in Observable prototype. I am using webpack for bundling. Do we have to do anything special for importing specific operator in webpack?
Code:
import { combineEpics } from 'redux-observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/mapTo';
const PING = 'PING';
const PONG = 'PONG';
const pingEpic = action$ => {
const obser = action$.ofType(PING);
return obser.mapTo({ type: PONG });
}
export const epic$ = new BehaviorSubject(combineEpics(pingEpic));
export const createRootEpic = (action$, store) =>
epic$.mergeMap(epic =>
epic(action$, store)
);
Update:
mapTo method is available to BehaviorSubject object (epic$.mapTo is available) but not to ActionsObservable object (action$.mapTo is not a function).

The issue is highly likely to be that you accidentally have two copies of RxJS. This can happen in several situations, but in your specific case it sounds highly likely to a recently reported issue in rxjs. That particular issue was fixed started in 5.5.1 (5.5.2 is now the latest).
Make sure you have RxJS 5.5.1 or above installed--you may need to remove your node_modules and reinstall to get a newer version locally if your semver range is broad. You can also triple check by looking at node_modules/rxjs/package.json the "version" field, usually at the bottom

Related

Client-only Nuxt 3 D3 plugin

I'm trying to use the D3 extension in a Nuxt 3 project and for that I created a d3.client.js file in the plugins/ directory.
import * as d3 from "d3";
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(d3)
})
However, when I try to use it gives me a 500 Internal Server Error document is not defined.
<script>
import * as d3 from "d3";
export default {
name: "globe",
created() {
d3.select("#globe");
}
}
</script>
How can I solve this?
d3.select() uses document.querySelector() under the hood. Since you're working server side, you don't have access to document yet. So you'll need to mock it or to avoid using it.
You can avoid using it all together by passing an element instead of a string to d3.select(), as it will then create a functioning d3 selection without running document.querySelector(). And since every other chained .select() or .selectAll() uses previousSelection.querySelector(), you can just continue from there.
If you do not have access to the DOM element directly, you might want to mock document. This article suggests using JSDOM:
import { JSDOM } from 'jsdom';
// create a new JSDOM instance for d3-selection to use
const document = new JSDOM().window.document;
d3.select(document.body)
.append('div');
I managed to solve it by using the d3.select with a Vue reference.
const globe = d3.select(this.$refs.globe)

Access JSON chunk exported from Gatsby Static Query

I have a React Component in a Gatsby app that is using the useStaticQuery hook to pull in data from the GraphQL layer. This component gets used in my application, but it also gets used as part of a JavaScript embed/widget that is created in a separate Webpack configuration.
I don't want the widget to depend on Gatsby, so I've shimmed the relevant bits of Gatsby, but I still need to pass in data to the shim I've created for useStaticQuery. I found that my Gatsby app is generating a file at public/static/d/2250905522.json that contains a perfect representation of the query data, and I'd like to use it like so:
// This file gets substituted when importing from `gatsby`
import queryResult from "../public/static/d/2250905522.json"
export const useStaticQuery = () => queryResult.data
export const graphql = () => {}
This works, but I haven't figured out where this is coming from or how to determine the file name in a way that is deterministic/stable. How is Gatsby determining this file name, and what internals might I use to do the same?
Edit: I found this routine in the Gatsby codebase that appears to be using staticQueryComponent.hash to determine the number. staticQueryComponent is being destructured from store.getState() where store is associated with Redux, but I'm still not sure where the hash is being determined yet.
Edit 2: Found another mention of this in the documentation here. It sounds like hash is a hash of the query itself, so this will change over time if the query changes (which is likely), so I'm still looking for the routine used to compute the hash.
Due to changes in the babel-plugin-remove-graphql-queries, coreyward's (awesome) answer should be updated to:
const { stripIgnoredCharacters } = require('graphql/utilities/stripIgnoredCharacters');
const murmurModule = require('babel-plugin-remove-graphql-queries/murmur');
const murmurhash = typeof murmurModule === 'function' ? murmurModule : murmurModule.murmurhash;
const GATSBY_HASH_SEED = 'abc';
function hashQuery(query) {
const result = murmurhash(stripIgnoredCharacters(query), GATSBY_HASH_SEED).toString();
return result;
}
module.exports = hashQuery;
The changes are:
fix the way murmurhash is imported. Credit to github user veloce, see: https://github.com/birkir/gatsby-source-graphql-universal/pull/16/files
Change to using stripIgnoredCharacters in order to match the updated way that gatsby internally hashes queries by first stripping whitespace and comment lines for efficiency.
Gatsby is using murmurhash with a seed of "abc" to calculate the hash of the full text of the query (including whitespace). This occurs in babel-plugin-remove-graphql-queries.
Since the reused components are isolated from Gatsby, the graphql tagged template literal can be shimmed in order to get the original query for hashing:
// webpack.config.js
module.exports = {
resolve: {
alias: {
gatsby: path.resolve(__dirname, "gatsby-shim.js"),
},
},
}
// gatsby-shim.js
import { murmurhash } from "babel-plugin-remove-graphql-queries/murmur"
import {
stripIgnoredCharacters,
} from "graphql/utilities/stripIgnoredCharacters"
const GATSBY_HASH_SEED = "abc"
const hashQuery = (query) =>
murmurhash(
stripIgnoredCharacters(query),
GATSBY_HASH_SEED
).toString()
export const graphql = query => hashQuery(query.raw[0])
This results in the query hash being passed into useStaticQuery, which can be shimmed similarly to retrieve the cached query from disk.
Also worth noting, newer versions of Gatsby store the StaticQuery result data in public/page-data/sq/d/[query hash].json.
If you're looking to do something similar, I've written up a much longer blog post about the details of this process and the solution I arrived at here.

Where is RxJS 6 static merge?

In RxJS 6, how do I import a static merge function for merging a list of Observables?
I want to be able to do:
const merged$ = merge(
obs1$,
obs2$,
obs3$
);
I've tried:
import { merge } from 'rxjs/observable/merge'; and
import { merge } from 'rxjs/operators';
but neither seems to give me what I want.
Importing has been made easy in RxJS 6:
import { merge } from 'rxjs';
You may want to read the official migration guide.
Another useful resource regarding importing in RxJS 6 is this talk by Ben Lesh who is the RxJS lead.
RxJS 7.X
In RxJS v7.X the merge() method is depricated and will be removed un RxJs v8.X, use mergeWith() instead.
See:
https://rxjs.dev/api/operators/mergeWith
https://rxjs.dev/api/operators/merge (depricated)
import { fromEvent } from 'rxjs';
import { map, mergeWith } from 'rxjs/operators';
const clicks$ = fromEvent(document, 'click').pipe(map(() => 'click'));
const mousemoves$ = fromEvent(document, 'mousemove').pipe(map(() => 'mousemove'));
const dblclicks$ = fromEvent(document, 'dblclick').pipe(map(() => 'dblclick'));
mousemoves$.pipe(
mergeWith(clicks$, dblclicks$),
)
.subscribe(x => console.log(x));
// result (assuming user interactions)
// "mousemove"
// "mousemove"
// "mousemove"
// "click"
// "click"
// "dblclick"
(example from api docs)
I believe now when the "creation" classes were removed the recommended way is importing directly from 'rxjs':
import { merge as mergeStatic } from 'rxjs';
Previous alpha version of RxJS 6 used to have 'rxjs/create' file but this has been removed already: https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md#600-alpha3-2018-02-06
However this expects you to use path maps correctly otherwise you'll import a lot of things you don't need. If you don't use path maps or the build process hidden from you you can import directly the correct file:
import { merge as mergeStatic } from 'rxjs/internal/observable/merge';
As of RXJS 6. The merge is in the rxjs/operators
import { map, take, merge, switchMap, filter } from 'rxjs/operators';

Redux Observable / RxJS: How to create custom observable?

I'm trying to do websocket setup in an redux-observable epic, and i'm going with an approach similar to this guy: https://github.com/MichalZalecki/connect-rxjs-to-react/issues/1
However, it looks like my first stab at wiring things up isn't working, even though it looks the same as the guy above:
import 'rxjs';
import Observable from 'rxjs';
import * as scheduleActions from '../ducks/schedule';
export default function connectSocket(action$, store) {
return action$.ofType(scheduleActions.CANCEL_RSVP)
.map(action => {
new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
.map(text => {
console.log("xxxxxxxxxxxxx: ", text);
return scheduleActions.rsvpCancelled(1);
});
};
However, I'm getting a Object is not a constructor error:
=== UPDATE ===
Looks like the suggestion to destructure the { Observable } export worked!
Not the only issue is that text doesn't seem to cross over to the next method...
import 'rxjs';
import { Observable } from 'rxjs';
import * as scheduleActions from '../ducks/schedule';
export default function connectSocket(action$, store) {
return action$.ofType(scheduleActions.CANCEL_RSVP)
.map(action => {
new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
.map(text => {
console.log("xxxxxxxxxxxxx: ", text); // prints undefined
return scheduleActions.rsvpCancelled(1);
});
};
In RxJS v5, the Observable class is available as named export, not the default export.
import { Observable } from 'rxjs';
Importing from regular rxjs will also import all of RxJS (adding all operators to the Observable prototype). This is described in the docs here. If you'd prefer to be more explicit and only import Observable itself you can import it directly at rxjs/Observable:
import { Observable } from 'rxjs/Observable';
Separately, you have a couple issues with the way you're mapping your custom Observable.
First Issue
You're not actually returning it. hehe. You're missing a return statement (or you can remove the curly braces and use arrow function implicit returns).
Second Issue
The regular .map() operator does not do anything special when you return an Observable. If you want the custom Observable to be subscribed to and flattened you'll need to use an operator that does flattening of some kind.
The most common two are mergeMap (aka flatMap) or switchMap.
action$.ofType(scheduleActions.CANCEL_RSVP)
.mergeMap(action => {
return new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
Which operator you need depends on your desired behavior. If you're not yet familiar, you can check out the documentation on the various operators or jump straight to the mergeMap and switchMap docs.
If you're adventurous, RxJS v5 does have WebSocket support out of box you can try with Observable.webSocket(). It's not documented very well, but you could also take a look at the unit tests, and for simple read-only unidirectional streaming it's pretty self explanatory--provide the URL and subscribe. It's actually incredibly powerful, if you can figure out how to use it, that is. Supports bi-directional, multiplex aka complex multiple input/output channels through a single socket. We use it at Netflix for several internal tools with thousands of rps.
You can take a look at Demo. Visit at Create Custom Observable

Error constructing an rxjs/Observable

Why does the following TypeScript code compile, but systemjs fails to load the dependencies correctly at runtime?
import { Observable } from 'rxjs';
let temp123 = new Observable<String>();
However, this works:
import { Observable } from 'rxjs/Observable';
let temp123 = new Observable<String>();
Specifically, the first code results in a .js file that contains the code:
var Observable_1 = require('rxjs');
var temp123 = new Observable_1.Observable();
but the second code generates this:
var Observable_1 = require('rxjs/Observable');
var temp123 = new Observable_1.Observable();
the line require('rxjs') fails with a 404 error because there is no file there. Why is the typescript compiler able to resolve this, but systemjs cannot load it at runtime?
Also noteworthy: This problem only happens if I do certain things with the Observable. For example, the following code works:
import { Observable } from 'rxjs';
let temp123: Observable<String> = null;
let xyz = temp123.first();
I can use the Observable, and call methods on it, without the TypeScript compiler generated a require('rxjs'). But I can't construct one, and I can't extend it either.
Versions:TypeScript 2.0.3, Systemjs 0.19.27, rxjs 5.0.0-beta.12
Why is the typescript compiler able to resolve this, but systemjs
cannot load it at runtime?
That's the way it works:
when you write import { Observable } from 'rxjs'; typescript finds rxjs folder in node_modules with package.json in it, which has
"typings": "Rx.d.ts"
that's type declarations file for rxjs, and that file contains
export { Observable } from './Observable';
which makes typescript to find another type declaration file in the same folder, Observable.d.ts, which has exported declaration for Observable class.
That's enough for your code to compile without errors.
If your code does not actually try to use Observable as a value, it will work, because typescript does unused reference elision - if Observable is used for type checking only, as in your second example, there will be no require('rxjs') call in generated javascrpt.
Now, SystemJS.
SystemJS does not have any default location to look for modules - it does not even recognise node_modules convention about package.json file with main property.
So, most likely, SystemJS in your example is configured like this:
SystemJS.config({
paths: {'npm:': 'node_modules/'},
map: {'rxjs': 'npm:rxjs'},
packages: {
rxjs: {
}
}
});
So, the module rxjs/Observable imported by this line
import { Observable } from 'rxjs/Observable';
is mapped to
node_modules/rxjs/Observable.js
because rxjs prefix matches map entry which together with paths maps it to node_modules/rxjs
Observable part comes through as is
.js extension is added because rxjs matches with rxjs package in systemjs config, and for any module that belongs to a package, SystemJS adds .js extension automatically unless defaultExtension is set to something else in that package config.
And it works, because the file node_modules/rxjs/Observable.js exists.
And that import works with typescript too, because node_modules/rxjs/Observable.d.ts exists too.
Finally, this does not work at runtime
import { Observable } from 'rxjs';
because it's mapped to node_modules/rxjs url, and there is no actual file there.
You can fix it by using main property in SystemJS package config:
packages: {
rxjs: {
main: 'Rx.js'
}
}
Now it's mapped to node_modules/rxjs/Rx.js, and that file actually exists and exports something named Observable, so it should work.
Checked with SystemJS 0.19.43, rxjs 5.0.3, typescript 2.1.5.

Resources