0

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.) Screenshot from Chrome DevTools

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

7
  • 1
    What, or where, is this todo list reducer? You mention state slices, are you using redux-toolkit by any chance? Can you edit the post to include a more complete and comprehensive minimal reproducible example that includes all relevant code being used? Commented May 17, 2023 at 5:59
  • @DrewReese I am not using RTK, I am using react-redux. I should note that this app worked perfectly until recently and I am not sure what changed and caused the issue. Commented May 17, 2023 at 16:49
  • 1
    Well, this is just one dev's opinion, but it's 2023 and we don't really bother writing bulky legacy redux code anymore; it's highly suggested to start using RTK as it significantly reduces the amount of boilerplate code you need to write. I don't see any overt issues here with the persist logic that would preclude specifically the state.todoList state from being persisted, e.g. it's not black listed in the persist configuration. Think you could stand up a running codesandbox demo that reproduces this issue that we could inspect live? Commented May 17, 2023 at 17:06
  • 1
    There unfortunately appears to be an issue trying to load the codesandbox you linked in your post. Commented May 18, 2023 at 5:15
  • 1
    OK, thanks. I'll take a look when I have availability. Commented May 19, 2023 at 16:48

1 Answer 1

1

Issues

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.

  1. 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.
  2. The 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.

Suggested Solution

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;
Sign up to request clarification or add additional context in comments.

1 Comment

That fixed it! Thank you for the detailed answer, you also helped me learn a little more about React state and how to manage it

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.