Skip to content

Clarify React’s definition of purity #7984

@gmoniava

Description

@gmoniava

I have some questions about purity in React that I couldn’t find answers to in the docs. It would be helpful if the docs clarified these points and/or someone answered them here.

My first question is, sometimes you may see such code:

import { useState } from "react";

export default function Demo() {
  const [value, setValue] = useState(() => localStorage.getItem("someKey") || "");
  return <div>{value}</div>;
}

I have seen similar code in popular libraries.
But react requires the initializer function to be pure.

So my first question is: is this case allowed?

Clarifying idempotence

The docs about purity say:

One of the key concepts that makes React, React is purity. A pure component or hook is one that is:

Idempotent – You always get the same result every time you run it with the same inputs – props, state, context for component inputs; and arguments for hook inputs.

Well, imho above example of initializer could be considered idempotent by above definition, if that definition assumes single mount. Because for single mount, above initializer function will return the same value for each re-render. But across multiple mounts, the returned value by the initializer function may be different. So does this definition apply for single mount or multiple?

Actually, there is even code example in the docs which suggests following approach to fix idempotency issue:

function useTime() {
  // 1. Keep track of the current date's state. `useState` receives an initializer function as its
  //    initial state. It only runs once when the hook is called, so only the current date at the
  //    time the hook is called is set first.
  const [time, setTime] = useState(() => new Date());
  //...

But again, across multiple mounts, this would produce different output. So is the idempotency about single mount?

No side effects in render: does it apply here?

Finally, even if we assume for a moment that initial example with localStorage is acceptable under the idempotence rule, is there some other rule that would make it impure? For example, that same section from docs says there should be no side effects in render:

Has no side effects in render – Code with side effects should run separately from rendering. For example as an event handler – where the user interacts with the UI and causes it to update; or as an Effect – which runs after render.

Someone could say my first code example from above is idempotent for single mount, but it violates another rule ("Has no side effects in render" rule) by reading localStorage in useState initializer. Would they be right?
Or we could respond that in this case since the initializer runs once, it is ok if it not strictly pure (assuming it is idempotent for some definition of idempotency).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions