Skip to content

No Pass Live State to Parent

Suggestion
small-rules/no-pass-live-state-to-parent

Disallow passing live state to parents in an effect.

Rationale

Ported from eslint-plugin-react-you-might-not-need-an-effect/no-pass-live-state-to-parent. The upstream rule points at the “Notifying parent components about state changes” section of the React docs, so that’s where the intent comes from.

Same family as passing data up, except the payload is the child’s own state. If the parent needs to react to the child’s state, the state lives in the wrong component. The effect-based notification is lifting state with extra steps: an extra render, and the parent updating from state it doesn’t own.

Lift the state. The parent owns it, passes the value and a setter down, and renders with what it needs on the same frame. React’s answer to two-way binding has always been that data flows down.

Diagnostic Messages

avoidPassingLiveStateToParentInComponent
Avoid passing live state to parents in an effect. Instead, lift "{{state}}" to the parent and pass it down to {{name}} as a prop.
avoidPassingLiveStateToParentInHook
Avoid passing live state to parents in an effect. Instead, return "{{state}}" from {{name}}.

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-pass-live-state-to-parent": [
"error",
{
"environment": "roblox-ts"
}
]
}
}

Examples

Effect passes live state to a parent
import React, { useEffect, useState } from "@rbxts/react";
export function Child({ onTextChanged }: { onTextChanged: (text: string | undefined) => void }): React.Element {
const [text, setText] = useState<string | undefined>();
useEffect(() => {
onTextChanged(text);
}, [onTextChanged, text]);
return <textbox Text={text} TextChanged={(textbox: { readonly Text: string }) => setText(textbox.Text)} />;
}
Parent owns the state
import React, { useState } from "@rbxts/react";
function Child({ text, onTextChanged }: { text: string; onTextChanged: (text: string) => void }): React.Element {
return (
<textbox
Text={text}
TextChanged={(textbox: { readonly Text: string }): void => {
onTextChanged(textbox.Text);
}}
/>
);
}
export function Parent(): React.Element {
const [text, setText] = useState("");
return <Child text={text} onTextChanged={setText} />;
}