avkonst / hookstate

The simple but very powerful and incredibly fast state management for React that is based on hooks
https://hookstate.js.org
MIT License
1.65k stars 109 forks source link

How to use object as state? #85

Closed westofpluto closed 4 years ago

westofpluto commented 4 years ago

I have a few questions on usage since the documentation is a bit vague on these. Suppose I want to maintain some global data in my app as follows:

employees currentEmployee tags currentTag

One option is to use this:

const globalState = createState({employees: [], currentEmployee: null, tags: [], currentTag: null);

Then when I get my employee object from the server, I can use this: some_employee = {get this object from the server} const state = useState(globalState); state.set(employee => some_employee);

Questions:

  1. Is this syntax correct?
  2. If I do this inside a React component, will it re-render only those components that use employee? Or will it re-render all the components that use any part of global state?
  3. If the answer to the above is that everything gets re-rendered, then how do I access only one field of the global state? In other words, how do I call useState with only the partition of globalState that I need to update?
  4. What happens if I use useState outside of a React component?
  5. Suppose I have a data access class that does not inherit from Component and a method in this class updates the global state. But inside my component I call this method of my data access class. Is it now necessary to use useState in my data access class method? In other words, it isn't clear to me when the current code is considered to be "inside a React component".
  6. Is it more efficient/better to keep global state variables in a single object (like above), or would it be better to have separate global state variables like this:

    const globalStateEmployees = createState([]); const globalStateCurrentEmployee = createState(null); const globalStateTags = createState([]); const globalStateCurrentTag = createState(null);

Why or why not?

avkonst commented 4 years ago

1.

state.set(...) call after the data is loaded is correct as long as it is done in an effect (see React.useEffect). You can use state.set(...) during render but it is not advised by React team.

You can use this feature of Hookstate to assist with async loading of the state: https://hookstate.js.org/docs/asynchronous-state Note: the syntax for this usecase is going to be simplified in version 3 of the library.

avkonst commented 4 years ago

2.

It will rerender only the components which use the segment of the state, which has been updated. You can read more here about the rendering optimisations: https://hookstate.js.org/docs/performance-intro

There is a complete matrix highlighting when a component is rerendered.

avkonst commented 4 years ago

N/A

avkonst commented 4 years ago

You will get an exception from React engine. useState is a hook and should follow rules of hooks. https://reactjs.org/docs/hooks-rules.html

You can use the Hookstate in a class based component with a little helper component. Read this: https://hookstate.js.org/docs/using-without-statehook

If you want to access/update the state in a global even handler like a timer spawned globally, you can use the result of the createState directly to read/write the state. See this example in the setInterval function: https://hookstate.js.org/docs/global-state

avkonst commented 4 years ago

5.

The code is inside of the React component is when you invoke useState from a) a functional React component OR b) from another function named 'useXXX', which itself is invoked from a functional react component

See how to wrap a hook by another hook to comply with Rules of React Hooks: https://hookstate.js.org/docs/exporting-state (notice useGlobalState function in the example).

You can have the analogy of useGlobalState in your data related class. It can be a method too, it just needs to start with prefix 'use'. For example: useEmployeeState

avkonst commented 4 years ago

6.

If your multiple states holding independent data (ie. updated independently and know nothing about each other), it maybe wise to keep it separate => easier to maintain in the future, refactor, split and move if you decide to.

See this real demo app https://hookstate.js.org/demo-todolist/ it has got 2 separate states: one for settings of the app, one for todo list.

If I design a google docs like application, I would have app settings state, user state, connection state and one state per each document (provided that one tab does not open more than 1 document).

It is unlikely you will not performance impact between having 1 or 3 useState calls for different separate state objects. However, it might become an issue if you mount many more hooks, especially if a component reacts to frequent state changes. All is up to you to measure.

My general rule is to keep number of useXxxxState like calls to no more than 5 in any function, just to keep the component shorter and simpler. But I allow for exceptions.

Now it is software engineering. A lot of things here is subjective and is based on experience. Try one approach aiming to keep things simple, not repetitive, easier to maintain. If it does not work change it. I change my approaches when software evolves. Usually it is not a big deal.

avkonst commented 4 years ago
  1. update:

I would keep 1 state about employees and the current employee as it looks like very much related. If tags are attributes of employees (or always used / updated when employees state is updated), I would keep tags in the same state too.

Note 1: when many things are combined in one state, there is no performance impact due to state usage tracking done by the Hookstate. So you are safe to use one global state, if you wish.

Note 2: you can also batch updates to the state: https://hookstate.js.org/docs/performance-batched-updates So, if your tags and employees data is updated in one event handler, you may wish to apply batching and it would be only possible if tags and employees are in one state object.

avkonst commented 4 years ago

Hope it answers your questions.

avkonst commented 4 years ago

Reopen if the answer is unclear