I'm using react-redux with redux-persist in a React application with multiple slices of state. It is a simple app with slices such as ui
, todolist
, notes
, and todolist-projects
. All of the slices of state are persisting except for the todolist
slice.
Below is my store/index.js
where I initialize the store and persistor:
import { legacy_createStore as createStore } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import { rootReducer } from "./rootReducer";
import storage from "redux-persist/lib/storage";
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(
persistedReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export const persistor = persistStore(store);
I read something inside of a GitHub issue about the key
prop sometimes needing to be set to "primary"
instead of "root"
. In the Chrome DevTools, in the localStorage debugging area, I noticed that the todolist
slice does seem to be persisting in the persist:primary
section. (see the screenshot below.)
I tried changing key
from "root"
to "primary"
but this does not seem to be working. It didn't seem to make a difference.
You can see this project live on CodeSandbox
The state.todolist
is being persisted to localStorage, as evidenced by your screenshot. There were a few issues I found in the code in your sandbox though.
NavBar
is using raw anchor (<a>
) tags instead of the react-router-dom
Link
component to effect navigation actions. These will reload the page and reset any local React component state.Todolist
component was using an incorrect object reference comparison to filter the selected state.todolist
state against the current state.ui.currTodoProject
value. These are both objects and after the state has been JSON serialized into localStorage and then JSON deserialized back into the Redux store, these will no longer have shallow reference equality... they are completely new and different objects.Update NavBar
to use the Link
component.
import "./NavBar.styles.css";
import { Link } from "react-router-dom";
import { persistor } from "../../../store/index.js";
const NavBar = () => {
const purgeCache = () => {
persistor
.purge()
.then(() => console.log("Cache Pruged!"))
.catch(() => console.log("Could Not Purge Cache..."));
};
return (
<nav>
<ul>
<li>
<Link to="/todolist">Todo List</Link>
</li>
<li>
<Link to="/notes">Notes</Link>
</li>
<li style={{ float: "right" }} onClick={purgeCache}>
<Link to="/">Purge Store</Link>
</li>
</ul>
</nav>
);
};
export default NavBar;
Update Todolist
compare the project objects *by their _id
properties. There's also no need to duplicate the todolist into any local state, simply compute the current currTodos
value from the selected todolist
state.
const currTodos = todolist.filter(
(t) => t.project._id === ui.currTodoProject._id
);
If computed this derived state is expensive use the useMemo
hook to memoize the computed value. In fact, just about any time you see yourself writing a useState
/useEffect
hook combo, this is what useMemo
is for.
const currTodos = useMemo(() => todolist.filter(
(t) => t.project._id === ui.currTodoProject._id
), [todolist, ui.currTodoProject._id]);
...
const Todolist = () => {
const todolist = useSelector((state) => state.todolist);
const projects = useSelector((state) => state.todolistProjects);
const ui = useSelector((state) => state.ui);
const dispatch = useDispatch();
const [deletedTodo, setDeletedTodo] = useState({});
useEffect(() => {
dispatch(changeTodoProject(projects[0]));
}, []);
const currTodos = todolist.filter(
(t) => t.project._id === ui.currTodoProject._id
);
...
return (
<div id="todolist" onClick={handleContainerClick}>
...
{currTodos.map((todo) => {
return (
<Todo
todo={todo}
key={todo._id}
showDeleteDialog={showDeleteDialog}
setDeletedTodo={setDeletedTodo}
/>
);
})}
...
</div>
);
};
export default Todolist;