skip to Main Content

I’m trying to implement a simple todo app with react-native and zustand persist, the code without the persist method is working fine, but when I try to introduce persist, I run into errors or I’m not able to correctly implement it, I went through the documentation on zustand page, but it is not working. Below is the relevant code and errors:

App.tsx

const App = () => {
  const { todos, addTodo, getTodos } = useTodoStore((state: TodoState) => state);
  
  useEffect(() => {
    getTodos();
  }, []);

  return (
    ...
  )
}

zustand store:

import create from 'zustand';
import { persist } from 'zustand/middleware';
import AsynStorage from '@react-native-async-storage/async-storage';

export interface Todo {
  id: string;
  text: string;
  completed: boolean;
  createdAt: string;
}

export interface TodoState {
  todos: Todo[];
  addTodo: (todo: Todo) => void;
  getTodos: () => void;
  removeTodo: (id: string) => void;
  toggleTodo: (id: string) => void;
}

export const useTodoStore = create<TodoState>(
  // @ts-ignore
  persist(
    set => ({
      todos: [],
      getTodos: () => {
        AsynStorage.getItem('todo-storage').then(todos => {
          if (todos) {
            set(state => ({ ...state, todos: JSON.parse(todos) }));
          }
        });
      },
      addTodo: (todo: Todo) => {
      set(state => {
        console.log('state: ', state.todos);
        return {
          ...state,
          todos: [...state.todos, todo],
          };
        });
      },
      removeTodo: (id: string) => {
        set(state => ({
          ...state,
          todos: state.todos.filter(todo => todo.id !== id),
        }));
      },
      toggleTodo: (id: string) => {
        set(state => ({
          ...state,
          todos: state.todos.map(todo =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo,
          ),
        }));
      },
    }),
    {
      name: 'todo-storage',
      getStorage: () => AsynStorage,  
    },
  ),
);

Now, when I try to add a todo, I run into the following error:

ERROR  TypeError: Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.

So, does anyone have a code example with persist (react-native), I don’t understand what I’m doing wrong here, I’m doing as shown in docs.

EDIT:
If I remove persist middleware and all code related to it, it works just fine, also, to note:

When I use persist middleware with async-storage, the state is this:

LOG  state:  {"state": {"_hasHydrated": true, "todos": {"state": [Object], "version": 0}}, "version": 0}

And when I don’t use persist it works:

LOG  state:  [{"completed": false, "createdAt": "Fri Jul 22 2022 14:27:47 GMT+0530 (IST)", "id": "6bfdab5e34b198", "text": "Let’s seeeee"}]

2

Answers


  1. I don’t know how zustand handles storages other than the default ones on the web, but I’m assuming perhaps the API doesn’t match with the react-native-async-storage. I would suggest you implement the get and set methods of your storage manually like they have explained in the docs here: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#how-can-i-use-a-custom-storage-engine

    Login or Signup to reply.
  2. I haven’t tried running your code yet. However, you don’t need to manually call AsyncStorage.getItem() to restore the saved store. The persist middleware handles that. getTodos, which I suspect is the errant action, is therefore unnecessary. I further suspect that zustand persist adds extra metadata to the serialized data, which is what you see when you manually log it out.

    See this example on the general structure (actions elided).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search