Undefined State Reselect Js - javascript

I am getting the following error: substate is undefined. However, I am not sure why substate would be undefined in my selector. Could someone please help me figure out what might be going wrong?
Selector:
import { createSelector } from 'reselect';
/**
* Direct selector to the organization state domain
*/
const selectOrganizationDomain = () => (state) => state.get('organization');
/**
* Other specific selectors
*/
/**
* Default selector used by organization
*/
const selectOrganization = () => createSelector(
selectOrganizationDomain(),
(substate) => substate.toJS()
);
const selectChanges = () => createSelector(
selectOrganization(),
(substate) => substate.get('changes')
)
export default selectOrganization;
export {
selectOrganizationDomain,
selectChanges
};

Your selectOrganizationDomain should be a function that returns .get('organization') on the state:
const selectOrganizationDomain = state => state.get('organization');
Your composed selectors should be the result of the invocation of createSelector, with the other selector functions passed in as arguments to createSelector:
const selectOrganization = createSelector(
selectOrganizationDomain,
substate => substate.toJS()
);
const selectChanges = createSelector(
selectOrganization,
substate => substate.get('changes')
);

The problem is your .toJS() in selectOrganization. I suppose organization in your state tree is immutable. It transforms your immutable object into a regualr JS object. For a regular object the get function is not defined.
Just get rid of selectOrganization and try selectChanges as:
const selectChanges = () => createSelector(
selectOrganizationDomain(),
(substate) => substate.get('changes')
)

I think createSelector is expecting you to pass a selctor for the first argument, not the result of calling a selector:
const selectChanges = () => createSelector(
selectOrganization, // no ()
(substate) => substate.get('changes')
)

I figured out the problem. In my routes.js, I forgot to inject my reducer/other modules. This was fixed with the following:
{
path: '/org',
name: 'organization',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Organization/reducer'),
System.import('containers/Organization/sagas'),
System.import('containers/Organization'),
]);
const renderRoute = loadModule(cb);
importModules.then(([reducer, sagas, component]) => {
injectReducer('organization', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
},

Related

how to memorize a value obtained from redux REACTJS

I want to obtain a value from a variable hosted in redux and memorize it, to later compare it with itself and verify if it is different.
I need, for example, to do the following : const value = useSelector(state => state.data.value); (suppose that here the value is 0) now, when value changes, i need to compare it with the value it had previously
If you want to check what the value was on the previous render, you can save it in a ref:
const SomeComponent = () => {
const value = useSelector(state => state.data.value)
const prevRef = useRef();
useEffect(() => {
// Once the render is complete, update the ref's value
prevRef.current = value;
});
// Do something comparing prevRef.current and value
}
If you're doing this a lot you might find it useful to make a custom hook:
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.curren;t
}
// used like:
const SomeComponent = () => {
const value = useSelector(state => state.data.value)
const prev = usePrevious(value);
// Do something comparing prev and value.
}
You have to use selectors(i use 'reselect' library for that), such as:
file: selectors.js
import { createSelector } from 'reselect';
const stateEnvironments = state => state.environments;
export const selectEnvironments = createSelector([stateEnvironments],
environments => environments.data);
so then in your component you can use mapStateToProps with reselect and connect
import { createStructuredSelector } from 'reselect';
import { connect } from 'react-redux';
// your component here
const EnvironmentCurrencies = props => {
const {
data,
} = props;
return (
<div>
...
</div.
);
};
const mapStateToProps = createStructuredSelector({
data: selectEnvironments,
});
// here you can update your values with actions
// mapDispatchToProps is especially useful for constraining our actions to the connected component.
// You can access these via `this.props`.
const mapDispatchToProps = dispatch => ({
setEnvironment: environment => dispatch(actions.setEnvironment(environment)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Environments);
this is not a full working example and the version that you might use, can have a bit different synaxis, but I hope this gives you a kick start. If not, let me know, I'll add more extensive example

Importing functions using Typescript

TLDR - what's the difference between these imports in ReactJs using Typescript:
setState1: (numbers: number[]) => void,
setState2: Function
Hi peeps, I've recently came across weird(for me) behaviour of importing functions using Typescript on React. To explain it better I'll just put sample code:
const FuncComp: React.FC = () => {
const [state1, setState1] = useState<number[]>([]);
const [state2, setState2] = useState<number[]>([]);
const handleSettingState1 = () => {
console.log("state1-",state1);
setState1([...state1, 1]);
}
console.log("state1-",state1);
const handleSettingState2 = () => {
console.log("state2-",state2);
setState1([...state2, 2]);
}
console.log("state2-",state2);
const saveChanges = () => {
saveHandler(
saveFunc,
setState1,
setState2
)
}
....
}
both handleSettingState1 and handleSettingState2 are functions passed to and set from child components, saveChanges is onClick function of button inside FuncComp and saveHandler is imported from helper file and it looks like this:
export const saveHandler = (
saveFunc: GqlMutationType,
setState1: (numbers: number[]) => void,
setState2: Function
) => {
saveFunc()
.then(() => {
setState1([]);
setState2([]);
})
.catch((err: AxiosError) => {
console.log(err);
});
};
As you can see setState1 and setState2 are imported differently.
Now the problem is that when I set both states through handlers -> perform save(it should clear both arrays in then callback) -> set another value, state1 will be empty both inside and outside handleSettingState1 scope, while state2 will have old value inside handleSettingState2 but empty inside the scope of the class.
I skipped implementation of handleSettingState2 and handleSettingState2 inside child component because I thought it's irrelevant but if it affects I can provide it too.
You can update the save handler like so.
export const saveHandler = (
saveFunc: GqlMutationType,
cb: (data: any) => void
) => {
saveFunc()
.then(data => cb(data))
.catch((err: AxiosError) => {
console.log(err);
});
};
Importing and calling the function
import { saveHandler } from './filename';
...
...
function saveFn() {}
saveHandler(saveFn, (data) => {
setState([...data]);
})

How to correctly use a curried selector function with react-redux's useSelector hook?

I am using react-redux with hooks, and I need a selector that takes a parameter that is not a prop. The documentation states
The selector function does not receive an ownProps argument. However,
props can be used through closure (see the examples below) or by using
a curried selector.
However, they don't provide an example. What is the proper way to curry as described in the docs?
This is what I've done and it seems to work, but is this right? Are there implications from returning a function from the useSelector function (it seems like it would never re-render?)
// selectors
export const getTodoById = state => id => {
let t = state.todo.byId[id];
// add display name to todo object
return { ...t, display: getFancyDisplayName(t) };
};
const getFancyDisplayName = t => `${t.id}: ${t.title}`;
// example component
const TodoComponent = () => {
// get id from react-router in URL
const id = match.params.id && decodeURIComponent(match.params.id);
const todo = useSelector(getTodoById)(id);
return <span>todo.display</span>;
}
When the return value of a selector is a new function, the component will always re-render on each store change.
useSelector() uses strict === reference equality checks by default, not shallow equality
You can verify this with a super simple selector:
const curriedSelector = state => () => 0;
let renders = 0;
const Component = () => {
// Returns a new function each time
// triggers a new render each time
const value = useSelector(curriedSelector)();
return `Value ${value} (render: ${++renders})`;
}
Even if the value is always 0, the component will re-render on each store action since useSelector is unaware that we're calling the function to get the real value.
But if we make sure that useSelector receives the final value instead of the function, then the component only gets rendered on real value change.
const curriedSelector = state => () => 0;
let renders = 0;
const Component = () => {
// Returns a computed value
// triggers a new render only if the value changed
const value = useSelector(state => curriedSelector(state)());
return `Value ${value} (render: ${++renders})`;
}
Conclusion is that it works, but it's super inefficient to return a new function (or any new non-primitives) from a selector used with useSelector each time it is called.
props can be used through closure (see the examples below) or by using a curried selector.
The documentation meant either:
closure useSelector(state => state.todos[props.id])
curried useSelector(state => curriedSelector(state)(props.id))
connect is always available, and if you changed your selector a little, it could work with both.
export const getTodoById = (state, { id }) => /* */
const Component = props => {
const todo = useSelector(state => getTodoById(state, props));
}
// or
connect(getTodoById)(Component)
Note that since you're returning an Object from your selector, you might want to change the default equality check of useSelector to a shallow equality check.
import { shallowEqual } from 'react-redux'
export function useShallowEqualSelector(selector) {
return useSelector(selector, shallowEqual)
}
or just
const todo = useSelector(state => getTodoById(state, id), shallowEqual);
If you're performing costly computations in the selector or the data is deeply nested and performance becomes a problem, take a look at Olivier's answer which uses memoization.
Here is a solution, it uses memoïzation to not re-render the component on each store change :
First I create a function to make selectors, because the selector depends on the component property id, so I want to have a new selector per component instances.
The selector will prevent the component to re-render when the todo or the id prop hasn't changed.
Lastly I use useMemo because I don't want to have more than one selector per component instance.
You can see the last example of the documentation to have more information
// selectors
const makeGetTodoByIdSelector = () => createSelector(
state => state.todo.byId,
(_, id) => id,
(todoById, id) => ({
...todoById[id],
display: getFancyDisplayName(todoById[id])
})
);
const getFancyDisplayName = t => `${t.id}: ${t.title}`;
// example component
const TodoComponent = () => {
// get id from react-router in URL
const id = match.params.id && decodeURIComponent(match.params.id);
const getTodoByIdSelector = useMemo(makeGetTodoByIdSelector, []);
const todo = useSelector(state => getTodoByIdSelector(state, id));
return <span>todo.display</span>;
}
Yes, it is how it's done, simplified example:
// Curried functions
const getStateById = state => id => state.todo.byId[id];
const getIdByState = id => state => state.todo.byId[id];
const SOME_ID = 42;
const TodoComponent = () => {
// id from API
const id = SOME_ID;
// Curried
const todoCurried = useSelector(getStateById)(id);
const todoCurried2 = useSelector(getIdByState(id));
// Closure
const todoClosure = useSelector(state => state.todo.byId[id]);
// Curried + Closure
const todoNormal = useSelector(state => getStateById(state)(id));
return (
<>
<span>{todoCurried.display}</span>
<span>{todoCurried2.display}</span>
<span>{todoClosure.display}</span>
<span>{todoNormal.display}</span>
</>
);
};
Full example:
This is helper-hook useParamSelector for TypeScript, which implements the official approach of Redux Toolkit.
Hook implementation:
// Define types and create new hook
export type ParametrizedSelector<A, R> = (state: AppState, arg: A) => R;
export const proxyParam: <T>(_: AppState, param: T) => T = (_, param) => param;
export function useParamSelector<A, R>(
selectorCreator: () => ParametrizedSelector<A, R>,
argument: A,
equalityFn: (left: R, right: R) => boolean = shallowEqual
): R {
const memoizedSelector = useMemo(() => {
const parametrizedSelector = selectorCreator();
return (state: AppState) => parametrizedSelector(state, argument);
}, [typeof argument === 'object' ? JSON.stringify(argument) : argument]);
return useSelector(memoizedSelector, equalityFn);
}
Create parametrized selector:
export const selectUserById = (): ParametrizedSelector<string, User> =>
createSelector(proxyParam, selectAllUsers, (id, users) => users.find((it) => it.id === id));
And use it:
const user = useParamSelector(selectUserById, 1001); // in components
const user = selectUserById()(getState(), 1001); // in thunks
You can also use it hook with selectors created with reselect's createSelector.

reselect selector with other selector as parameter doesn't execute

I'm trying to use a reselect selector as an argument to another selector:
import { createSelector } from 'reselect'
const productsSelector = state => {
return state.get('searchResults').get('products')
}
export const getSelectedProduct = createSelector(
productsSelector,
(products) => {
const selected = products.filter(product => product.shop.selected)
return selected[0]
}
)
export const getSelectedProductProduct = createSelector(
getSelectedProduct,
prod => {
const x = prod ? prod.products ? prod.products.find(produc => produc.selected) : getSelectedProduct : {}
if(prod) {
console.log(prod)
if ('prod', prod.products) {
console.log(prod.products)
if (prod.products.find(produ => produ.selected)) {
console.log('sel prod', prod.products.find(produ => produ.selected))
}
}
}
console.log(x)
}
)
getSelectedProduct is working and updating when it should update. However getSelectedProductProduct is not executing. What am I doing wrong?
They're used in a component:
const mapStateToProps = (state) => ({
region: state.get('map').get('region'),
markers: state.get('searchResults').get('products'),
selectedProduct: getSelectedProduct(state),
selectedProductProduct: getSelectedProductProduct(state)
})
action diff
redux state
The redux action which causes the diff in the top image does not cause getSelectedProductProduct to execute. I don't think the component knows that its field changed. I'm investigating this further. Wondering if the field is too deeply nested for the component to register the change.
It looks like getSelectedProduct filters searchResults.products.shop but the action is changing searchResults.products.products[1].selected. So getSelectedProduct returns the same value unchanged every time. That is why getSelectedProductProduct doesn't re-compute.

Use reselect selector with parameters

How do I pass additional parameters to combined selectors? I am trying to
• Get data
• Filter data
• Add custom value to my data set / group data by myValue
export const allData = state => state.dataTable
export const filterText = state => state.filter.get('text')
export const selectAllData = createSelector(
allData,
(data) => data
)
export const selectAllDataFiltered = createSelector(
[ selectAllData, filterText ],
(data, text) => {
return data.filter(item => {
return item.name === text
})
}
)
export const selectWithValue = createSelector(
[ selectAllDataFiltered ],
(data, myValue) => {
console.log(myValue)
return data
}
)
let data = selectWithValue(state, 'myValue')
console.log(myValue) returns undefined
Updated: 16 February 2022
New Solution from Reselect 4.1: See detail
// selector.js
const selectItemsByCategory = createSelector(
[
// Usual first input - extract value from `state`
state => state.items,
// Take the second arg, `category`, and forward to the output selector
(state, category) => category
],
// Output selector gets (`items, category)` as args
(items, category) => items.filter(item => item.category === category)
);
// App.js
const items = selectItemsByCategory(state, 'javascript');
// Another way if you're using redux hook:
const items = useSelector(state => selectItemsByCategory(state, 'javascript'));
Updated: 6 March 2021
Solution from Reselect: See detail
// selector.js
import { createSelector } from 'reselect'
import memoize from 'lodash.memoize'
const expensiveSelector = createSelector(
state => state.items,
items => memoize(
minValue => items.filter(item => item.value > minValue)
)
)
// App.js
const expensiveFilter = expensiveSelector(state)
// Another way if you're using redux:
// const expensiveFilter = useSelector(expensiveSelector)
const slightlyExpensive = expensiveFilter(100)
const veryExpensive = expensiveFilter(1000000)
Old:
This is my approach. Creating a function with parameters and return function of reselect.
export const selectWithValue = (CUSTOM_PARAMETER) => createSelector(
selectAllDataFiltered,
(data) => {
console.log(CUSTOM_PARAMETER)
return data[CUSTOM_PARAMETER]
}
)
const data = selectWithValue('myValue')(myState);
Here's one with the latest useSelector hook.
The important thing is to get the parameter from the input selector. The input selector's second parameter is how we get it.
Here's how the selector would look,
const selectNumOfTodosWithIsDoneValue = createSelector(
(state) => state.todos,
(_, isDone) => isDone, // this is the parameter we need
(todos, isDone) => todos.filter((todo) => todo.isDone === isDone).length
)
And here's how we extract values with the useSelector hook,
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const NumOfTodosWithIsDoneValue = useSelector((state) =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return <div>{NumOfTodosWithIsDoneValue}</div>
}
Also, keep, the second parameter (isDone) as primitive values (string, number etc.) as much as possible.
Because, reselect, only runs the output selector when the input selector value changes.
This change is checked via shallow comparison, which will always be false for reference values like Object and Array.
References:
https://react-redux.js.org/next/api/hooks#using-memoizing-selectors
https://flufd.github.io/reselect-with-multiple-parameters/
https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/
The answer to your questions is detailed in an FAQ here: https://github.com/reactjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument
In short, reselect doesn't support arbitrary arguments passed to selectors. The recommended approach is, instead of passing an argument, store that same data in your Redux state.
what about returning a function from selector? getFilteredToDos is an example for that
// redux part
const state = {
todos: [
{ state: 'done', text: 'foo' },
{ state: 'time out', text: 'bar' },
],
};
// selector for todos
const getToDos = createSelector(
getState,
(state) => state.todos,
);
// selector for filtered todos
const getFilteredToDos = createSelector(
getToDos,
(todos) => (todoState) => todos.filter((toDo) => toDo.state === todoState);
);
// and in component
const mapStateToProps = (state, ownProps) => ({
...ownProps,
doneToDos: getFilteredToDos()('done')
});
This is covered in the reselect docs under Accessing React Props in Selectors:
import { createSelector } from 'reselect'
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter
const getTodos = (state, props) =>
state.todoLists[props.listId].todos
const makeGetVisibleTodos = () => {
return createSelector(
[ getVisibilityFilter, getTodos ],
(visibilityFilter, todos) => {
switch (visibilityFilter) {
case 'SHOW_COMPLETED':
return todos.filter(todo => todo.completed)
case 'SHOW_ACTIVE':
return todos.filter(todo => !todo.completed)
default:
return todos
}
}
)
}
export default makeGetVisibleTodos
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos()
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
return mapStateToProps
}
In this case, the props passed to the selectors are the props passed to a React component, but the props can come from anywhere:
const getVisibleTodos = makeGetVisibleTodos()
const todos = getVisibleTodos(state, {listId: 55})
Looking at the types below for Reselect:
export type ParametricSelector<S, P, R> = (state: S, props: P, ...args: any[]) => R;
export function createSelector<S, P, R1, T>(
selectors: [ParametricSelector<S, P, R1>],
combiner: (res: R1) => T,
): OutputParametricSelector<S, P, T, (res: R1) => T>;
We can see there isn't a constraint on the type of props (the P type in ParametricSelect), so it doesn't need to be an object.
Another option:
const parameterizedSelector = (state, someParam) => createSelector(
[otherSelector],
(otherSelectorResult) => someParam + otherSelectorResult
);
And then use like
const mapStateToProps = state => ({
parameterizedSelectorResult: parameterizedSelector(state, 'hello')
});
I am not sure about memoization/performance in this case though, but it works.

Categories

Resources