dai-shi / react-tracked

State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.
https://react-tracked.js.org
MIT License
2.73k stars 72 forks source link

How to get the original state #185

Closed marcinkieruzel closed 7 months ago

marcinkieruzel commented 1 year ago

Hi, I've recently experienced an issue with React Track. Perhaps its more about understanding the library and proxies than a real problem but – In some cases I want to have the up to date context event if it's not rendered in current component. For example in useEffect hook or events. Hence an onClick event in PersonFamilyName. It will always give me an "old" value of firstName. How can I get up to date context in this circumstances?


import React, {
  useReducer,
  useRef,
  useEffect,
  Reducer,
} from 'react';

import { createContainer, getUntrackedObject } from 'react-tracked';

const initialState = {
  firstName: 'Harry',
  familyName: 'Potter',
};

type State = typeof initialState;
type Action =
  | { type: 'setFirstName'; firstName: string }
  | { type: 'setFamilyName'; familyName: string };

const reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case 'setFirstName':
      return { ...state, firstName: action.firstName };
    case 'setFamilyName':
      return { ...state, familyName: action.familyName };
    default:
      throw new Error('unexpected action type');
  }
};

const { Provider, useTracked } = createContainer(
  () => useReducer(reducer, initialState),
  { concurrentMode: true },
);

const PersonFirstName = () => {
  const [state, dispatch] = useTracked();
  const renders = useRef(1);

  useEffect(() => {
    renders.current += 1;
  });

  return (
    <div>
      First Name: {Math.random()}
      <input
        value={state.firstName}
        onChange={(event) => {
          dispatch({ type: 'setFirstName', firstName: event.target.value });
        }}
      />
      (renders:{renders.current})
    </div>
  );
};

const PersonFamilyName = () => {
  const [state, dispatch] = useTracked();
  const renders = useRef(1);

  useEffect(() => {
    console.log("Test FamilyName")
    renders.current += 1;
  });

  return (
    <div>
      Family Name: {Math.random()}

      <button onClick={() => {
        console.log("I want to get the actual state", state.firstName)
        console.log("I want to get the actual state", getUntrackedObject(state))
      }}>Click</button>

      <input
        value={state.familyName}
        onChange={(event) => {
          dispatch({ type: 'setFamilyName', familyName: event.target.value });
        }}
      />
      (renders:{renders.current})
    </div>
  );
};

const ReactTracked = () => (
  <Provider>
    <PersonFirstName />
    <PersonFamilyName />
  </Provider>
);

export default ReactTracked;```
dai-shi commented 1 year ago

In some cases I want to have the up to date context event if it's not rendered in current component.

I don't think it's an issue with react-tracked. It's how useState (useReducer) works. I mean, it will behave the same with bare context.

What you want is probably a "global state" and in such cases, you may want to try zustand (with or without react-tracked), or jotai, or valtio.

dai-shi commented 7 months ago

Closing as stale.