Reselect error: Selector creators expect all input-selectors to be functions - reactjs

I'm trying to use reselect within my component:
const test = useSelector(({ test }) => test);
const testData = createSelector(
test,
items => console.log(items),
);
I'm getting Error: Selector creators expect all input-selectors to be functions, instead received the following types: [object]. I don't know if it is important but test comes async.
What can I do to make it work?
Thank you!

When processing JavaScript, the interpreter will read all functions in scope before executing any statements.
It's important to note that if you use the ES6 style of defining functions as lambdas
then your "functions" don't get processed until those assignment statements are executed.
instead of
const makeSelectUserEmail = () => {
createSelector(
selectAuthDomain,
authState => authState.email,
);
}
use
const makeSelectUserEmail = () =>
createSelector(
selectAuthDomain,
authState => authState.email,
);

Your code appears to be kind of backwards.
useSelector expects a selector function as its argument, calls the selector, and returns the result.
That snippet appears to be trying to use the return value from useSelector as an input to createSelector, which is wrong.
In addition, you shouldn't call createSelector inside of a component, as that will create a new memoized selector instance every time the component renders.
Finally, while I realize this is likely just an attempt to verify things are working, you shouldn't be doing work like console.log() inside of a selector. A selector should just return a value.
If this test value doesn't exist right away, then your selector and component logic should be written in a way that handles cases where it may not exist.

I had the same issue, but based on the react-redux documentation, this is how you should use Reselect with useSelector:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumCompletedTodos = createSelector(
(state) => state.todos,
(todos) => todos.filter((todo) => todo.completed).length
)
export const CompletedTodosCounter = () => {
const numCompletedTodos = useSelector(selectNumCompletedTodos)
return <div>{numCompletedTodos}</div>
}
export const App = () => {
return (
<>
<span>Number of completed todos:</span>
<CompletedTodosCounter />
</>
)
}

Related

Using the Context API as a way of mimicking useSelector and useDispatch with redux v5

I'm working on a React project where I'm constrained to using React Redux v5, which doesn't include useDispatch and useSelector.
Nonetheless I really would like to have these hooks (or something like them) available in my app.
Therefore, I've created a wrapper component at the top level of the app which I connect using redux's connect function.
My mapStateToProps and mapDispatchToProps then just look like this:
const mapDispatchToProps = (dispatch: DispatchType) => {
return {
dispatch,
};
};
const mapStateToProps = (state: StateType) => {
return {
state,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainLayout);
In my wrapper component, I then pass the dispatch and the state into the value:
<DispatchContext.Provider value={{ state, dispatch }}>
{children}
</DispatchContext.Provider>
Finally, I have a hook that looks like this:
const useSelectAndDispatch = () => {
const context = useContext(DispatchContext);
if (context === null) {
throw new Error("Please use useDispatch within DispatchContext");
}
const { state, dispatch } = context;
function select(selector) {
return selector(state);
}
return { dispatch, select };
};
I then use dispatch and selector in my components via useSelectAndDispatch.
I was wondering if this is an appropriate way to go about this issue, and whether I can expect any performance problems. I am using reselect, and have a good understanding of memoization. I'm just looking for opinions, since I've heard that the redux team held off implementing useDispatch and useSelector for a long time because of performance issues.
Many thanks for any opinions!
This will cause significant peformance problems. Your mapStateToProps is using the entire state object, so every time anything changes in the state, the provider must rerender. And since the provider rerendered with a new value, so too must every component that consumes the context. In short, you will be forcing most of your app to rerender anytime anything changes.
Instead of using mapStateToProps and mapDispatchToProps, i would go back to the actual store object, and build your hooks from that. Somewhere in your app is presumably a line of code that says const store = createStore(/* some options */).
Using that store variable, you can then make some hooks. If i can assume that there's only one store in your app, then the dispatch hook is trivial:
import { store } from 'wherever the store is created'
export const useMyDispatch = () => {
return store.dispatch;
}
And the selector one would be something like this. It uses .subscribe to be notified when something in the store changes, and then it uses the selector to pluck out the part of the state that you care about. If nothing changed, then the old state and new state will pass an === check, and react skips rendering. If it has changed though, the component renders (only the component that called useMySelect plus its children, not the entire app)
export const useMySelector = (selector) => {
const [value, setValue] = useState(() => {
return selector(store.getState());
});
useEffect(() => {
const unsubscribe = store.subscribe(() => {
const newValue = selector(store.getState());
setValue(newValue);
});
return unsubscribe;
}, []);
return value;
}

why react hook init before redux props

this is some kind of code so be gentle :D
i have some page that use react hooks and redux and the redux state work
i checked it it load data. the scenario is i have page that load input boxes from redux thunk and save the changes with hooks and redux thunk
const MyComponent = (props) => {
const { reduxProp } = props
const [t1, setT1] = useState('')
useEffect( () => {
reduxActionLoadData // t1 , t2 data
setT1(reduxProp.t1)
setT2(reduxProp.t2)
},[])
return (
<div>
<input value={t1} onChange={ (e) => { setT1(e.target.value) } />
<input value={t2} onChange={ (e) => { setT2(e.target.value) } />
</div>
)
}
const mapStateToProp = (state) => (
{
reduxProp: state.reduxProp
})
const mapDispatchToProp = (dispatch) => (
{
reduxActionLoadData = connectActionCreators(reduxActionLoadDataAction, dispatch)
})
export default const connect(mapStateToProps, mapDispatchToProps)(MyComponent)
when i check props it is loaded, redux state is loaded too
but hooks init with empty value
i try to init them with reduxProp.t1, reduxProp.t2 no luck there too
the thing is when i run app it works fine but when i refresh the page it fails
and sometimg it fails when i run app too
i try to use loading state and do stuff after loading = true no luck the either
For one, your mapDispatchToProp currently does not seem to do anything, since with the = it will be parsed as a method body, not as an object - so you are not connecting your actions at all. It should look more like this:
const mapDispatchToProp = (dispatch) => (
{
reduxActionLoadData: connectActionCreators(reduxActionLoadDataAction, dispatch)
})
Then you should instead be using the map object notation:
const mapDispatchToProp = {
reduxActionLoadData: reduxActionLoadDataAction
}
and the you also have to actually use that in your component:
useEffect( () => {
props.reduxActionLoadData()
},[])
That will still create an asynchronous effect, so reduxProp.t1 will not change immediately.
You will probably have to update those later in a separate useEffect.
useEffect( () => {
setT1(reduxProp.t1)
},[reduxProp.t1])
All that said, there is no reason to use connect at all, since you could also use the useSelector and useDispatch hooks. connect is a legacy api that you really only need to use with legacy class components.
Generally I would assume that you are right now learning a very outdated style of Redux by following an outdated tutorial. Modern Redux does not use switch..case reducers, ACTION_TYPE constants, hand-written action creators, immutable reducer logic or connect - and it is maybe 1/4 of the code of old-style Redux.
Please follow the official Redux tutorial for an understanding of mondern Redux. https://redux.js.org/tutorials/essentials/part-1-overview-concepts

How can I memoize a variable derived from redux useSelector?

How can I memoize my rawTranscript variable so it doesn't trigger the useEffect below which subsequently triggers the expensive transcriptParser function? I've been trying a lot of different approaches, but the fact that I am using a redux-hook (useAppSelector) to capture the data from the store means I cannot use an empty dependency useEffect for the initial mount (hooks can't be inside of useEffect). I also can't seem to wrap the useAppSelector with a useMemo either for the same reason.
Any thought's on how I can memoize the rawTranscript variable so it doesn't re-trigger the useEffect?
error when using the redux-hook inside useMemo, useEffect, useCallback:
React Hook "useAppSelector" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
component
const TranscriptCardController = (): JSX.Element => {
const dispatch = useAppDispatch();
// how to memoize rawTranscript?
const rawTranscript = useAppSelector(selectRawTranscript);
const parsedTranscript = useAppSelector(selectParsedTranscript);
useEffect(() => {
const parsedResult = transcriptParser(rawTranscript, undefined, 0.9);
dispatch(updateParsedTranscript(parsedResult));
}, [dispatch, rawTranscript]);
// ...
};
selector
export const selectRawTranscript = createSelector(
(state: RootState) => state.transcript.rawTranscript,
(rawTranscript): RawTranscript => rawTranscript
);
There is no issue here if your selectRawTranscript function is purely selecting a value from the store, like state => state.transcript.raw. Your effect will only run when the value of rawTranscript changes -- as it should.
If your selectRawTranscript function returns a new object every time (like it if it involves array mapping, etc.) then this is a problem that you can address either in the selector itself or in the component.
Memoized Selectors
The best place to fix this is by using createSelector to create a memoized selector. For example:
import {createSelector} from '#reduxjs/toolkit';
export const selectRawTranscript = createSelector(
(state: RootState) => state.data.someRawValue,
(rawValue) => rawValue.map(entry => entry.data)
);
The second part of the selector is the "combiner" and it will only re-run when the value selected in the first part changes. So you get a consistent object reference.
Equality Comparisons
If you want to fix this in the component, the way to do that is by including a second argument on useAppSelector (which I'm assuming is just a typed version of useSelector).
This second argument allows you to specify a custom equality function so that you have more control over when the selected data is considered to be "changed". It's common to use a shallow equality comparison, so this is actually included in the react-redux package.
import { shallowEqual } from 'react-redux';
import { useAppSelector } from ...
const TranscriptCardController = (): JSX.Element => {
const rawTranscript = useAppSelector(selectRawTranscript, shallowEqual);
...
Note: it's impossible for me to know whether or not you really do have a problem with undesirable changes in rawTranscript because you haven't included your selector function. You might be overthinking this and it might be a non-issue.
Create a standalone useCallback where your dispatch will run on every store update but useEffect will only execute when the callback method is executed.
const TranscriptCardController = (): JSX.Element => {
const dispatch = useAppDispatch();
// how to memoize rawTranscript?
const rawTranscript = useAppSelector(selectRawTranscript);
const parsedTranscript = useAppSelector(selectParsedTranscript);
const callback = useCallback(() => {
const parsedResult = transcriptParser(rawTranscript, undefined, 0.9);
dispatch(updateParsedTranscript(parsedResult));
}, [rawTranscript])
useEffect(() => {
const unsubscribe = callback()
return unsubscribe
}, [callback]);
// ...
};

React functional component with useSelector and useDispatch creates loop

I'm sure this is a case of my brain just not getting it... BUT...
I'm used to using class Components and not functional components in general and with React Redux I'm trying to code a component that dispatches an action. The action of course causes a reducer to update the Redux state (store) as you probably know. Trying to replace mapStateToProps and mapDispatchToProps with useSelector and useDispatch however has me creating a loop... I'm guessing that I'm using useSelector incorrectly.
import { fetchPostsByTerm } from "../../_actions/_postActions";
import { useSelector, useDispatch } from "react-redux";
const payload = { vocabulary: "tags", term: "thiphif" };
export const PostsByTerm = () => {
const dispatch = useDispatch();
dispatch(fetchPostsByTerm(payload));
const posts = useSelector((state) => state.postsByTerm);
return (
<div>
<h1 className="post_heading">Posts</h1>
{posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
</div>
);
};
maybe I am using it correctly? there are other components updating state on the same page
You must not dispatch directly in the functional component. Instead use a useEffect hook to perform a dispatch. If your objective is to only dispatch the action on initial render, pass on the dependency to useEffect as an empty array
export const PostsByTerm = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchPostsByTerm(payload));
}, [])
const posts = useSelector((state) => state.postsByTerm);
return (
<div>
<h1 className="post_heading">Posts</h1>
{posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
</div>
);
};
FYI - Commenting here because it was an "aha" moment for me to understand the purpose of the array [] in the useEffect utility. The array is used to house state variables which, when changed, will force execution of the function listed. So in my case actually since I wanted fetchPostsByTerm to execute when the selected term changed (not obvious since my example shows it hardcoded)
useEffect(() => {
dispatch(fetchPostsByTerm(payload));
}, [term])
... was what I eventually went with. And now it's working great! The articles for the selected term get fetch when a new term is selected.

How to do fetch with React Hooks; ESLint enforcing `exhaustive-deps` rule, which causes infinite loop

I'm pretty new to React hooks in general, and very new to useSelector and useDispatch in react-redux, but I'm having trouble executing a simple get request when my component loads. I want the get to happen only once (when the component initially loads). I thought I knew how to do that, but I'm running into an ESLint issue that's preventing me from doing what I understand to be legal code.
I have this hook where I'm trying to abstract my state code:
export const useState = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.data);
return {
data: data,
get: (props) => dispatch(actionCreators.get(props))
};
};
Behind the above function, there's a network request that happens via redux-saga and axios, and has been running in production code for some time. So far, so good. Now I want to use it in a functional component, so I wrote this:
import * as React from 'react';
import { useState } from './my-state-file';
export default () => {
const myState = useState();
React.useEffect(
() => {
myState.get();
return () => {};
},
[]
);
return <div>hello, world</div>;
};
What I expected to happen was that because my useEffect has an empty array as the second argument, it would only execute once, so the get would happen when the component loaded, and that's it.
However, I have ESLint running on save in Atom, and every time I save, it changes that second [] argument to be [myState], the result of which is:
import * as React from 'react';
import { useState } from './my-state-file';
export default () => {
const myState = useState();
React.useEffect(
() => {
myState.get();
return () => {};
},
[myState]
);
return <div>hello, world</div>;
};
If I load this component, then the get runs every single render, which of course is the exact opposite of what I want to have happen. I opened this file in a text editor that does not have ESLint running on save, so when I was able to save useEffect with a blank [], it worked.
So I'm befuddled. My guess is the pattern I'm using above is not correct, but I have no idea what the "right" pattern is.
Any help is appreciated.
Thanks!
UPDATE:
Based on Robert Cooper's answer, and the linked article from Dan Abramov, I did some more experimenting. I'm not all the way there yet, but I managed to get things working.
The big change was that I needed to add a useCallback around my dispatch functions, like so:
export const useState = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.data);
const get = React.useCallback((props) => dispatch({type: 'MY_ACTION', payload:props}), [
dispatch
]);
return {
data: data,
get: get,
};
};
I must admit, I don't fully understand why I need useCallback there, but it works.
Anyway, then my component looks like this:
import * as React from 'react';
import { useState } from './my-state-file';
export default () => {
const {get, data} = useState();
React.useEffect(
() => {
get();
return () => {};
},
[get]
);
return <div>{do something with data...}</div>;
};
The real code is a bit more complex, and I'm hoping to abstract the useEffect call out of the component altogether and put it into either the useState custom hook, or another hook imported from the same my-state-file file.
I believe the problem you're encountering is that the value of myState in your dependency array isn't the same value or has a different JavaScript object reference on every render. The way to get around this would be to pass a memoized or cached version of myState as a dependency to your useEffect.
You could try using useMemo to return a memoized version of your state return by your custom useState. This might look something like this:
export const useState = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.data);
return useMemo(() => ({
data: data,
get: (props) => dispatch(actionCreators.get(props))
}), [props]);
};
Here's what Dan Abramov has to say regarding infinite loops in useEffect methods:
Question: Why do I sometimes get an infinite refetching loop?
This can happen if you’re doing data fetching in an effect without the second dependencies argument. Without it, effects run after every render — and setting the state will trigger the effects again. An infinite loop may also happen if you specify a value that always changes in the dependency array. You can tell which one by removing them one by one. However, removing a dependency you use (or blindly specifying []) is usually the wrong fix. Instead, fix the problem at its source. For example, functions can cause this problem, and putting them inside effects, hoisting them out, or wrapping them with useCallback helps. To avoid recreating objects, useMemo can serve a similar purpose.
Full article here: https://overreacted.io/a-complete-guide-to-useeffect/

Resources