YousefED / SyncedStore

SyncedStore CRDT is an easy-to-use library for building live, collaborative applications that sync automatically.
https://syncedstore.org
MIT License
1.75k stars 54 forks source link

`useMemo` not running when dependency array changes #105

Closed extrange closed 1 year ago

extrange commented 1 year ago

It seems that using the state returned by useSyncedStore in a dependency array does not trigger a recalculation of memoized values.

For the following code:

import { useMemo } from "react";
import { useSyncedStore } from "@syncedstore/react";
import { syncedStore } from "@syncedstore/core";

const store = syncedStore({ todos: [] });

export default function App() {
  const state = useSyncedStore(store);
  const sum = useMemo(() => state.todos.reduce((a, b) => a + b, 0), [
    state.todos
  ]);
  return (
    <div className="App">
      <button onClick={() => state.todos.push(1)}>Add 1</button>
      <div>Sum: {sum}</div>
    </div>
  );
}

When the 'Add 1' button is clicked, the 'Sum' doesn't update.

CodeSandbox

dignakov commented 1 year ago

I think I'm seeing the same thing but with useEffect.

YousefED commented 1 year ago

Hi!

Because SyncedStore uses observables, the actual array (state.todos) doesn't change if a value is added or removed.

I think in this cause it should work without the useMemo, and the component will automatically rerender when the data has changed

extrange commented 1 year ago

Thanks!

I notice that re-renders happen when subscribing to a property of an object in an array, e.g. when a component uses state.todos[0].name, if state.todos.push({name: 'new todo'}) is called elsewhere, the component will still rerender. This happens even if the component is wrapped in React.memo.

Not sure if this is a bug or I am doing something wrong?

Codesandbox

An example: clicking on 'Add Todo' causes the the component displaying the first todo to rerender, even though this should only happen when its title is modified.