Error: Selector Creators expect all Input-Selectors to be Functions

Redux

I came across this Reselect.js error earlier today:

Error: Selector creators expect all input-selectors to be functions, instead received the following types: [function, undefined]

Addressing this problem is a nightmare if you don't know what to look for. But lucky for you, I've dealt with this before.

I admit I wrote the following code -- well, the names have been changed to protect the innocent. Yes, here it looks stupid and obvious. But, in the real code base, I assure you it's much less obvious (but still stupid)

This is what the basic pattern looks like:

someSelectors.ts

import { createSelector } from 'reselect';import { State } from './state';import { getA } from './otherSelectors.ts';export const getB = (state: State) => state.b;export const getD =    createSelector(        getA,        getB,        (a, b) => a + b;    );

otherSelectors.ts

import { createSelector } from 'reselect';import { getB } from './someSelectors.ts';export const getA = (state: State) => state.a;export const getC =    createSelector(        getA,        getB,        (a, b) => a - b;    );

dependentCode.ts

import { getD } from './someSelectors.ts'console.log(getD(state)); // Error

Circular Dependency

Circular Reference Problem

The problem is a circular dependency. someSelectors.ts imports from otherSelectors.ts and vice versa. When dependentCode.ts imports from the circle, one of the files will not be able to be resolved.

Whichever import gets trashed will cause an undefined to show up instead of the expected function.

Fixing the Circular Dependency

Breaking this kind of circular dependency is as easy as reorganizing your files. For instance, could move getA and getC into someSelectors.ts.

Sometimes, however, it makes more sense to move both getA and getB into a new file of their own and leave getC and getD where they are.

Circular Reference Solution

That should fix the problem for 99% of the cases.

ES6+ Caveat

When processing JavaScript, the interpreter will read all functions in a scope before executing any statements.

It's important to note that if you use the ES6 style of defining functions as lambdas

const f = (x: number) => x + 42;

as opposed to

function f(x: number) {    return x + 42;}

then your "functions" don't get processed until those assignment statements are executed.

You can read more about this phenomenon called Function Hoisting.

This means that you could still have circular dependencies even if you put all the dependent selectors in the same file.

If that's the case, you need to refactor out the core dependency.

Web development is *way* more complicated than it used to be. But you can learn from someone who's been there and done that.

Sign up on my email list where I write about modern web development with technology like React, Redux, TypeScript, Webpack and more.