arnelenero / simpler-state

The simplest app state management for React
https://simpler-state.js.org
MIT License
478 stars 16 forks source link

[Persistence] data doesn't get fetched in use until I trigger an action (entity.set()) #9

Closed jalasem closed 3 years ago

jalasem commented 3 years ago

Describe the bug In order to try this out, I set up a simple todo list on Codesandbox.io. Everything worked fine until I introduce the persistence plugin. It doesn't fetch the persisted state when the component mounts unless I trigger the entity.set() function.

To Reproduce Steps to reproduce the behaviour:

  1. link to a sandbox to demonstrate this: https://codesandbox.io/s/loving-worker-vei0p?file=/src/App.tsx
  2. add a couple of todos from the input field
  3. click reload in the codesandbox mini browser, only the initial state will show up.

Expected behaviour todo items are supposed to be fetched when components mounts (on load)

arnelenero commented 3 years ago

Thanks for reporting this. Will look into this. I prefixed the issue title with [Persistence] just for added up-front detail.

jalasem commented 3 years ago

My pleasure. I really love this. The fact that nobody has to wrap his/her head around the state, reducers, actiontypes, actions and what's not is what I'm really exited about.

This is a great job and I really commend it. I look forward to this issue being resolved.

jalasem commented 3 years ago

While this is being fixed/attended to, a quick hack around this is to fetch data, parse it and use it to initialize the entity always.

import { entity, persistence } from 'simpler-state';

const stored = window.localStorage.getItem("todos");

const initState = stored ? JSON.parse(stored) : [];

export const todoEntity = entity(initState, [persistence('todos')])

export const setTodo = (newTodo => 
  todoEntity.set((currentTodoState) => ({
      ...currentTodoState,
      newTodo,
    })
  ))

// actions
export const addTodo = (newItem, front = true) =>
  front
    ? todoEntity.set((todos) => [newItem, ...todos])
    : todoEntity.set((todos) => [...todos, newItem]);

export const removeTodo = (index) =>
  todoEntity.set((todos) =>
    todos.filter((todo, todoIndex) => index !== todoIndex)
  );

export const editTodo = (index, value) =>
  todoEntity.set((todos) =>
    todos.map((todo, todoIndex) => {
      if (todoIndex === index) todo = value;

      return todo;
    })
  );
arnelenero commented 3 years ago

I have identified and fixed the race condition that causes this. v1.0.1 is available now.

Here's an updated sandbox (only change was the version of simpler-state in package.json): https://codesandbox.io/s/cocky-almeida-36e7i?file=/src/App.tsx

Thanks again, @jalasem for bringing this to my attention.

jalasem commented 3 years ago

It's my pleasure

jalasem commented 3 years ago

I currently use simpler-state on a branch of a production app in my company. I'll definitely let you know if I detect any other issue.

Well done @arnelenero 👍🏽

jalasem commented 3 years ago

Your fix is still slow. It works but causes a glitch in the UI. Mine works perfectly.

arnelenero commented 3 years ago

@jalasem Thanks for your patience in testing this. I just released v1.0.2, which optimizes persistence to localStorage/sessionStorage. This should now behave the same way as your workaround.

jalasem commented 3 years ago

I will test this and give you feedback.

jalasem commented 3 years ago

Yesterday, I held a workshop where I shared simpler-state with my community. They really share the same excitement as I do. However, someone asked me a very valid question;

Apart from the developer experience, what other benefit does simpler-state has. i.e performance and benchmark. Has any test been carried out? @arnelenero

arnelenero commented 3 years ago

Yesterday, I held a workshop where I shared simpler-state with my community. They really share the same excitement as I do. However, someone asked me a very valid question;

Apart from the developer experience, what other benefit does simpler-state has. i.e performance and benchmark. Has any test been carried out? @arnelenero

I ran some open-source tests that were originally meant to test state managers in Concurrent Mode, but it also measures execution times. Happy to report these positive results:

All figures are execution times in milliseconds (lower is better)

  External Events Transition Auto Increment
SimpleR State 2885 2319 2746
React-redux 3231 2335 3143
Redux + use-mutable-source 3286 2403 3225
Recoil 3209 2562 * 3105
Use-subscription 3205 2413 3242
Zustand 3178 2340 3126
Jotai 3210 2429 3230
Valtio 3217 2338 3305
React Sweet State 4180 3276 3150
Effector 4174 3140 3104

(*) Recoil failed the transition test for some reason tests were based on: https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-mode

So SimpleR State is not only developer-friendly, but also superlatively fast. I don't really like comparing with other libraries out there, especially the "giants"—it simply wasn't the goal of the library—but since someone asked... 🤣

jalasem commented 3 years ago

Amaaazing. I'm sold out.

One more thing, how do we use simpler-state with asyncStorage in react native. I tried that and it did not work due to its asynchronous nature?

arnelenero commented 3 years ago

asyncStorage is compatible with Web Storage API, and SimpleR State supports async custom storage, so you should be able to use asyncStorage. Do you have a sample code?

arnelenero commented 3 years ago

Ckosing this for cleanup. Feel free to re-open or create a new issue if you see further issues. Thanks.