Skip to content

No Initialize State

Suggestion
small-rules/no-initialize-state

Disallow initializing state in an effect.

Rationale

Ported from eslint-plugin-react-you-might-not-need-an-effect/no-initialize-state. The upstream rule points at TkDodo’s post on avoiding hydration mismatches with useSyncExternalStore, which is a web-React problem.

The shape is a mount-only effect that synchronously sets state. You already have a place for initial values: the useState() initializer. An effect that initializes state means the first frame renders without the value, then the effect fires and it pops in a frame later. On the web that’s also a hydration mismatch, which is what the upstream link is about; roblox-ts has no server render, so that half doesn’t apply here, but the initial-value problem is identical.

The one legitimate case is async initialization, you can’t await in an initializer, so the rule backs off when the state write happens inside an async callback. That’s the exception, not the rule.

Diagnostic Messages

avoidInitializingState
Avoid initializing state in an effect. Instead, initialize "{{state}}"'s "useState()" with "{{arguments}}". For SSR hydration, prefer "useSyncExternalStore".

Configuration

This rule accepts one options object after the severity.

environmentOptional

The React environment: 'roblox-ts' uses @rbxts/react, 'standard' uses react.

{
"jsPlugins": [
"@pobammer-ts/small-rules"
],
"rules": {
"small-rules/no-initialize-state": [
"error",
{
"environment": "roblox-ts"
}
]
}
}

Examples

State initialized by an effect
import React, { useEffect, useState } from "@rbxts/react";
export function MyComponent(): React.Element {
const [state, setState] = useState<string | undefined>();
useEffect(() => {
setState("Hello");
}, []);
return <textlabel Text={state} />;
}
State initialized from an async source
import React, { useEffect, useState } from "@rbxts/react";
declare const game: {
readonly GetService: (service: string) => { readonly GetAsync: (url: string) => Promise<string> };
};
export function MyComponent(): React.Element {
const [state, setState] = useState<string | undefined>();
useEffect(() => {
void (async (): Promise<void> => {
const response = await game.GetService("HttpService").GetAsync("https://api.example.com/data");
setState(response);
})();
}, []);
return <textlabel Text={state} />;
}