How can reselect createSelector be able to be treated as the data or array when it returns a function? - reselect

When we use the npm package reselect (with React), we may do
export const someData = createSelector(
objs,
(objs) => {
// ...
});
and I think someData is actually a function? But inside of our Component's .jsx file, we could show it as:
{someData[0].countThumbUp}
so it is somewhat strange, when someData is a function, but we can somehow treat it as an array. How does it work?

Related

Retrieve State From Redux Store

I am learning Redux global state store and I created a simple app, when click the "Select" button, the selected item will be added to the SavedCourses list. Then I want to display the SavedCourses list. But right now my code does not display the list. Is there something wrong with my reducer? My sandbox link is at https://ky801.csb.app/#/
It's actually your mapDispatchToProps function which is the problem. addCourse is supposed to take an argument which is the class_nbr. But your this.props.addCourse function ignores its arguments and just calls dispatch(addCourse()).
const mapDispatchToProps = (dispatch) => {
return {
addCourse: () => dispatch(addCourse())
};
};
Since you aren't actually passing the course info, your savedCourses is ending up like [undefined, undefined, undefined].
You could change your mapDispatchToProps so that it takes an argument:
const mapDispatchToProps = (dispatch) => {
return {
addCourse: (class_nbr) => dispatch(addCourse(class_nbr))
};
};
But when all you are doing is dispatching the function with the same arguments then it is not necessary to define mapDispatchToProps as a function. You can just use an object of action creators:
export default connect(mapStateToProps, {addCourse})(Courses);
Redux has gotten a lot easier nowadays with the react-redux hooks and redux-toolkit. You can simplify your code a lot using those.

How to bind to and use a higher-order component in ReasonReact

Let's say I have a higher-order component, something like the following trivial definition, exported from the JavaScript module ./hoc.js:
export const withStrong =
Component => props =>
<strong> <Component ...props/> </strong>
Assuming I have some component called HelloMessage, what is the equivalent of this piece of JavaScript:
import { withStrong } from './hoc.js';
const HelloMessage = ...
const StrongMessage = withStrong(HelloMessage);
ReactDOM.render(
<StrongMessage name="Joe" />,
document.getElementById('react-app')
);
TL;DR:
This should be the exact equivalent of the requested JavaScript snippet:
[#bs.module ./hoc.js]
external withStrong
: React.component('props) => React.component('props)
= "withStrong";
module HelloMessage = ...
module StrongMessage = {
include HelloMessage;
let make = withStrong(make);
};
ReactDOMRe.renderToElementWithId(
<StrongMessage name="Joe" />,
"react-app"
);
There'a also a runnable example on the Reason playground with a few adaptations made to work around not having a separate JavaScript file.
Explanation follows:
Binding
withStrong is just a function. It happens to be a function that accepts and returns a react component, which is a bit mysterious, but they're really just values like any other. We can just bind it like an ordinary function.
Even something as simple as this would work
[#bs.module ./hoc.js]
external withStrong : 'a => 'a = "withStrong";
assuming you always make sure to pass in a component. But it wouldn't be particularly safe as you can pass it anything else too, so let's try to use the type system as it should be used, restricting it to only accept react components.
The ReasonReact source code says components have the type component('props), so that's what we'll use.
[#bs.module ./hoc.js]
external withStrong
: React.component('props) => React.component('props)
= "withStrong";
Using the 'props type variable in both the argument and return type means we constrain them to be the same. That is, the returned component will have exactly the same props as the one passed in, which is exactly what we want in this case.
And that's really all there is to the binding itself. we can now use it like this:
let strongMessage = withStrong(HelloMessage.make);
Unfortunately this doesn't support JSX. To render strongMessage as is we'd have to write something like
React.createElementVariadic(strongMessage, { "name": "Joe" }, [||]);
Not great. So let's fix that.
JSX
<StrongMessage name="Joe" />
transforms to
React.createElementVariadic(
StrongMessage.make,
StrongMessage.makeProps(~name="Joe", ()),
[||]
);
So we need a StrongMessage module with two functions, make and makeProps that conform to what's expected by React.createElementVariadic. make is just the component itself, so that's simple enough. makeProps is a function that acccepts the props as labeled arguments terminated by unit (since the props may be optional) and returns a js object. This also happens to be exactly what [#bs.obj] does, which isn't in any way coincidental.
Putting this together then, we get:
module StrongMessage = {
let make = withStrong(HelloMessage.make);
[#bs.obj]
external makeProps
: (~name: string, unit) => {. "name" string }
= "";
}
And that's it! Yay!
Addendum: Shortcuts
Ok, so the makeProps function is a bit of an annoying mouthful. Fortunately in our case, where the props of the wrapped component is the same as the original, it's also unnecessary since StrongMessage.makeProps will be identical to HelloMessage.makeProps. Let's just steal that then! And now we have
module StrongMessage = {
let make = withStrong(HelloMessage.make);
let makeProps = HelloMessage.makeProps;
}
But we can do even better! By using include HelloMessage we can drop makeProps entirely (thanks to #bloodyowl, via #idkjs, for this one).
module StrongMessage = {
include HelloMessage;
let make = withStrong(make);
}
That's pretty nice, isn't it? This works because include HelloMessage will include all the exported definitions from HelloMessage such as makeProps, but also make and anything else. This is probably what you want when you wrap a component in this way, but beware that it imports and re-exports everything from the included module, in case that's not what you want.
Usage
Finally, once we have both the binding and JSX in order we can use it like this
ReactDOMRe.renderToElementWithId(
<StrongMessage name="Joe" />,
"react-app"
);

console.log(..) of 'reselect' selector displays f()

I'm using react-boilerplate that uses reselect for reading the redux state and maps it to props. I am trying to read the redux state outside a react component (say in custom function defined in myUtil.js).
There is selector in selectors.js:
const makeSelectLoading = () => createSelector(selectGlobal, globalState => globalState.get('loading'));
I import makeSelectLoading into myUtil.js and try to print the value using console.log(makeSelectLoading()). I am expecting the value of the Loading state variable, instead I'm getting the below:
myUtil.js?d403:14 ƒ () {
if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
// apply arguments instead of spreading for performance.
lastResult = func.apply(null, arguments);
}
Should I expect this to work? If not, any ideas where I'm going wrong? Thanks!
makeSelectLoading is not a selector itself but a selector factory: a function which returns an actual selector.
In order to use it you should get a selector instance from it and then call the returned instance with the expected arguments.
const getSelectLoading = makeSelectLoading();
const loadingState = getSelectLoading(state);

How to test GraphQL queries with fragments using jest

Problem: I would like to test a GraphQL query that lives in a .graphql file like this:
#import '../../fragments/Widget.graphql'
query WidgetFragment($id: ID) {
readWidgetFragment(id: $id) {
...Widget
}
}
To create a GraphQL schema with mocked resolvers and data, I use makeExecutableSchema and addMockFunctionsToSchema from graphql-tools.
To run the query from inside a jest test, my understanding is that I need to use the graphql() function from graphql-js.
This function needs the query as a string, so I tried two different ways, but neither of them worked:
Parse the .graphql file as a normal text file, giving me the raw string (using the jest-raw-loader in my jest config).
This gives me: Failed: Errors in query: Unknown fragment "Widget". when I run the query.
Parse the .graphql file into a query object using jest-transform-graphql. I believe this should be the right approach, because it should resolve any imported fragments properly. However, to execute the query, I need to pass query.loc.source.body to the graphql, which results in the same error message as option 1.
You can use this:
import { print } from 'graphql/language/printer'
import query from './query.gql'
...
print(query)
Use the initial approach with parsing it as a raw text, except:
use a recursive function with a path argument (assuming you could have nested fragments)
which uses regex to extract all imports beforehand to an array (maybe use a nicer pattern :) )
append the rest of the file to a string variable
then loop through imports, resolving the #imports and passing them to itself and appending the result to the string variable
Finally return the result to the main function where you pass it to the graphql()
Yes, this is quite a pickle. Even with imports correctly working (>= v2.1.0 for jest-transform-graphql, they get added to the query.definitions object, which is completely sidestepped when calling graphql with document.loc.source.body as query argument.
On the server end, graphql (function graphqlImpl) will reconstruct the document object using parse(source) - but it'll have zero knowledge of the imported fragment definitions...
As far as I can tell, the best bet is to stamp fragments to the query source before sending it to the server. You'll need to explicitly find all lines starting with #import and replace these with actual text content of the to-be-imported graphql file.
Below is the function that I use. (Not tested for recursive fragments)
// Async wrapper around dynamic `import` function
import { importQuery } from "./queries";
const importAndReplace = async (fileToImport, sourceDocument, line) => {
const doc = await importQuery(fileToImport);
const targetDocument = (await sourceDocument).replace(line, doc.loc.source.body);
return targetDocument;
};
// Inspired by `graphql-tag/loader`
// Uses promises because of async function `importQuery` used
export default async graphqlOperation => {
const { body } = graphqlOperation.loc.source;
const lines = body.split(/\r\n|\r|\n/);
const bodyWithInlineImports = await lines.reduce(
async (accumulator, line) => {
await accumulator;
const lineSplit = line.slice(1).split(" ");
return line[0] === "#" && lineSplit[0] === "import"
? importAndReplace(lineSplit[1].replace(/"/g, ""), accumulator, line)
: Promise.resolve(accumulator);
},
Promise.resolve(body)
);
return bodyWithInlineImports;
};

how to ignore ordering of reselect selectors when composing selectors

As I compose more selectors together, I'm finding that I'm reordering where the selectors are defined. For example,
export const selectNav = state => state.nav;
export const selectPage = state => state.page;
export const selectNavAndPage = createSelector([
selectNav,
selectPage,
], (nav, page) => {
});
export const selectFoo = state => state.foo;
export const selectNavAndPageAndFoo = createSelector([
selectNavAndPage,
selectFoo,
], (navAndPage, foo) => {
});
This is a simple example, but I could not define selectNavAndPage below selectNavAndPageAndFoo. As more selectors get composed and selectors of selectors get composed, then I need to make sure all the sub-selectors are defined at the top before I use them.
Is there some way to create these selectors such that ordering doesn't matter?
I was worried about the same problem and I created this npm module define-selectors. This is a module that delays the definition of the selector to solve the ordering of selector definition problem and adds other features to it. It has not been stable yet, but I will use it on my project to make it stable and be improved.
For more information please go to github page and read the README and source files.
I'm pretty sure this is just related to how the ES6 const keyword works. With const, variables do not exist until that line, so if you want to reference a const variable, you need to write that code after the variable declaration. With var, all variables are hosted to the top of the scope.
So, either use var so that you can reference things out of order, or continue using const and define each function in the correct order for usage and references.
If you don't mind a little extra typing, here is another approach which requires defining a 'cms' utility function that wraps the createSelector function and takes advantage of function hoisting:
import {createSelector} from 'reselect';
// create memoized selector
function cms(ctx, ...args) {
if (!ctx.selector) ctx.selector = createSelector(...args);
return ctx.selector;
}
// define the selectors out of order...
export function getBaz(state) {
return cms(
getBaz // the function itself as context
, getBar
, bar => bar.baz
)(state);
}
export function getBar(state) {
return cms(
getBar
, getFoo
, foo => foo.bar
)(state);
}
export function getFoo(state) {
return state.foo;
}
This is not as elegant as simply defining the selectors in order, but maybe someone else can take this idea and improve on it.

Resources