I am using Redux 5 in my React 18 application. When using the useSelector()
method from the react-redux
package, I get the below warning in the browser console:
Selector unknown returned the root state when called. This can lead to unnecessary rerenders.
Selectors that return the entire state are almost certainly a mistake, as they will cause a rerender whenever *anything* in state changes.
I have also seen the redux documentation for this warning at https://react-redux.js.org/api/hooks#identity-function-state--state-check, however, the problem I am facing is that "the state is an array" in my code. Please see my files below:
Notes.jsx
import { useDispatch, useSelector } from 'react-redux';
import { toggleImportanceOf } from '../reducers/noteReducer';
const Note = ({ note, handleClick }) => {
return (
<li onClick={handleClick}>
{note.content}
<strong> {note.important ? 'important' : ''}</strong>
</li>
);
};
const Notes = () => {
const dispatch = useDispatch();
const notes = useSelector((state) => {
console.log(state); // THIS IS AN ARRAY
return state;
});
return (
<ul>
{notes.map((note) => (
<Note
key={note.id}
note={note}
handleClick={() => dispatch(toggleImportanceOf(note.id))}
/>
))}
</ul>
);
};
export default Notes;
noteReducer.js
is my reducer
const noteReducer = (state = [], action) => {
switch (action.type) {
case 'NEW_NOTE':
return [...state, action.payload];
case 'TOGGLE_IMPORTANCE': {
const id = action.payload.id;
const noteToChange = state.find((n) => n.id === id);
const changedNote = {
...noteToChange,
important: !noteToChange.important,
};
return state.map((note) => (note.id !== id ? note : changedNote));
}
default:
return state;
}
};
const generateId = () => Number((Math.random() * 1000000).toFixed(0));
export const createNote = (content) => {
return {
type: 'NEW_NOTE',
payload: {
content,
important: false,
id: generateId(),
},
};
};
export const toggleImportanceOf = (id) => {
return {
type: 'TOGGLE_IMPORTANCE',
payload: { id },
};
};
export default noteReducer;
main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import App from './App';
import noteReducer from './reducers/noteReducer';
const store = createStore(noteReducer);
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
);
Please note, I know that createStore
is deprecated. This is just an introductory lesson and we will be learning about the new method that has replaced the createStore
method.
The array of the state is as below:
The array contents get filled in a form and the list is then rendered on the UI. Please see image below:
QUESTION: How can I fix this warning in my browser console for a 'state' that is an array?
Basically you should just not ever have a selector function that returns the entire state object.
Nest the notes
state under a notes
property in the state and select that.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { createStore, combineReducers } from "redux";
import App from "./App";
import noteReducer from "./reducers/noteReducer";
// Create a root reducer function
const rootReducer = combineReducers({
notes: noteReducer // <-- note reducer forms state.notes
});
const store = createStore(rootReducer);
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>
);
import { useDispatch, useSelector } from "react-redux";
import { toggleImportanceOf } from "./reducers/noteReducer";
import { useEffect } from "react";
const Note = ({ note, handleClick }) => {
return (
<li onClick={handleClick}>
{note.content}
<strong> {note.important ? "important" : ""}</strong>
</li>
);
};
const Notes = () => {
const dispatch = useDispatch();
const notes = useSelector((state) => state.notes); // <-- select state.notes array
useEffect(() => {
console.log(notes);
}, [notes]);
return (
<ul>
{notes.map((note) => (
<Note
key={note.id}
note={note}
handleClick={() => dispatch(toggleImportanceOf(note.id))}
/>
))}
</ul>
);
};
export default Notes;
Note also that you are also learning what is effectively "deprecated" Redux. Modern Redux is written using the Redux-Toolkit library, which is more streamlined and much less boilerplatey.