lostpebble / pullstate

Simple state stores using immer and React hooks - re-use parts of your state by pulling it anywhere you like!

Home Page:https://lostpebble.github.io/pullstate

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

does useMemo improve perf?

prabirshrestha opened this issue · comments

Is there any difference between the following code when passing getSubState?

wihout useMemo().

const value = MyStore.useState(toFormatStr('%'));

vs

with useMemo()

const value = MyStore.useState(useMemo(() => toFormatStr('%'), []));
Here is the full code
const MyStore = new Store({
  value: 10
});

const toFormatStr = (value) => {
  console.log('outer');
  return s => {
    console.log('inner');
    return s.value + ' ' + value;
  }
}

export default function App() {
  const value = MyStore.useState(toFormatStr('%'));
  // const value = MyStore.useState(useMemo(() => toFormatStr('%'), []));
  console.log(value);

  const [count, setCount] = useState(0);

  return (
    <div className="App">
      value: {value}
      <button onClick={e=>setCount(count + 1)}>Re-render</button>
    </div>
  );
}

Is useMemo() good in this case or is not worth the optimization?

Interesting... I really don't think it will be anything noticeable, as I think passing in anonymous functions like that is already quite well optimized in the JavaScript runtime.

It might actually be a detriment, because useMemo itself is also a function that is being called each time, at least on the first run you are objectively losing performance (again, probably completely irrelevant in actual loss).

I think the best option here, if you really care about the cost of defining the selection function (because that's all its doing, its not actually speeding up the selection of sub-state or anything). Then I'd say just define your fully expanded selection functions somewhere else, perhaps close to your store:

const MyStore = new Store({
  value: 10
});

const toFormatStr = (value) => {
  console.log('outer');
  return s => {
    console.log('inner');
    return s.value + ' ' + value;
  }
}

const Selector = {
  valueWithPercent: toFormatStr('%'),
}

// Then in your component again

export default function App() {
  const value = MyStore.useState(Selector.valueWithPercent);
  console.log(value);

  const [count, setCount] = useState(0);

  return (
    <div className="App">
      value: {value}
      <button onClick={e=>setCount(count + 1)}>Re-render</button>
    </div>
  );
}

If you do want more flexibility, and your "creator" functions (such as toFormatStr('%')) are quite large and verbose in their complexity, then I suppose useMemo could be a good option.

toFormatStr('%') might not be the best example but what if % comes from a prop?

Or even a better example may be extracting full name.

function fullName(id) {
   return state => state.itemsById[id].firstName + ' ' + state.itemsById[id].lastName;
}

function App({ id }) {
   const name = useStoreState(MyState, fullName(id), [id]);
}

At least for now I haven't come up where console.log('outer') is expensive so might not be an issue. But was curious if pullstate does something extra internally since the callback changed. I haven't been using useMemo currently so I think should be good. Wanted to hear your thoughts if you had seen perf improvements.

Also might be good to update the pullstate docs to show this pattern when getSubState takes a function ref instead of inlining the function. Similar to store.update(toggleMode).

toFormatStr('%') might not be the best example but what if % comes from a prop?

Or even a better example may be extracting full name.

I think the best would be, as I said at the end of my post: If you do want more flexibility, and your "creator" functions (such as toFormatStr('%')) are quite large and verbose in their complexity, then I suppose useMemo could be a good option.

function App({ id }) {
   const name = useStoreState(MyState, fullName(id), [id]);
}

This is good use of Pullstate. You pass in the [id] dependencies and if they change then the function will be re-evaluated. That is part of Pullstate's internal performance optimizations.

I think you might be over thinking the performance aspect here a bit, there's not much else you can do in this situation really - unless of course the "creator" function is really complex, as mentioned before.

I haven't used this pattern in my projects loads yet, personally. useMemo is also overkill in a lot of situations.

Also might be good to update the pullstate docs to show this pattern when getSubState takes a function ref instead of inlining the function. Similar to store.update(toggleMode).

Yea the docs need a bit of a refresher on many things, I'll try and do a big update soon.

sounds good. thanks for taking time to answer. will profile and optimize as necessary.