# Start Your React Rule File With State. Here Is How to Write That Section.

> Source: <https://dev.to/avery_code/start-your-react-rule-file-with-state-here-is-how-to-write-that-section-2ngb>
> Published: 2026-09-08 07:45:38+00:00

A friend of mine decided last spring that he was finally going to write down his coding standards so the AI would stop handing him components that looked like they came from four different projects. He opened a markdown file, typed a heading, sat there for maybe ten minutes, and then went and did something else. When I asked him about it a few weeks later the file was still open in a tab somewhere with nothing under the heading.

That happens to almost everyone who tries this. "Write down your standards" sounds like one task and is actually about forty of them stacked on top of each other. Architecture, naming, typing, accessibility, error handling, folder structure, and no particular reason to start with any one over the others.

My suggestion is to start with state. It is the section where you find out fastest whether the thing you just wrote is a real rule or a sentence you happen to agree with. This is the first part of a series that works through a rule file one section at a time, and each part hands you something you can drop into your own file the same day.

It usually looks like this.

```
Keep state in the right place.
```

The AI agrees with this completely. It agreed before you typed it. Ask it directly whether state belongs in the right place and you get a reasonable little explanation about lifting state up and colocation, and then it hands you a component with a filter value, a sort order, a modal flag and a fetch all in the same function body, because nothing in that sentence told it where the line falls in this particular case.

The test I use now took me an embarrassingly long time to arrive at. A rule belongs in the file if a session that reads it produces different code than a session that does not. That is the whole bar. Everything that fails it is a note to yourself, which is fine, but it should live somewhere other than the file the AI reads before every task.

Here is what I ended up with instead.

```
State is local if it is only required within one feature or
screen. State becomes global only when at least two independent
features need it and the need is long term.
```

Roughly the same idea, except now there is something in there you can hold up against actual code. Two independent features. Long term.

The word independent carries most of the weight, and I only worked that out after the first version of the rule said two components and kept pushing things into the store that had no business being there. Two components inside one feature sharing a value is an ordinary situation with an ordinary answer, which is a hook. Two features that otherwise do not know about each other needing the same data is a different problem with different consequences. If the rule does not separate those cases, then over a few months everything migrates upward into the global store, one reasonable-looking decision at a time.

Six rules. This is what is actually in the file, not a tidied version for the article.

```
## State and Effects

1. State is local if it is only required within one feature or
   screen. State becomes global only when at least two independent
   features need it and the need is long term. Auth state is not
   automatically global.

2. There is never more than one mutable copy of the same domain
   data. If two places can both change the same information, one
   of them is wrong. Original data is a snapshot for comparison,
   not a second state source.

3. Derived state is not stored. Anything that can be computed
   from existing state or props is computed during render. Dirty
   flags and comparison results are derived, never persisted.

4. No useEffect for logic that can be synchronously derived from
   props or state. If the effect body only reads state and writes
   state, delete it and compute the value instead.

5. Loading, error, and success are explicit typed states. A
   component that can be in one of them declares that in its
   types rather than inferring it from whether data happens to
   be null.

6. Form components are presentational. Initial values come from
   outside. State transformation happens in the hook, not inside
   the form.
```

Around two hundred words, which is deliberate. There was a much longer version at one point and I noticed nobody on the team could tell me what was in it, including me.

The sentence about auth in rule one exists because of a specific afternoon I spent untangling an auth store in a client project. Auth goes global on day one in almost every codebase I have worked in, usually before anyone knows which features will actually need it, and once it is sitting there it becomes the argument for the next thing that goes in. Sometimes global genuinely is the right call. If you have a header, a router guard, three feature areas and a permissions system all reading the same user object, that is what a store is for. What you often have instead is a token that one module cares about and a user object read by two screens, and the whole thing could have stayed inside the auth feature behind a hook. Writing the exception into the rule means someone has to make that decision on purpose.

Rule two prevents the category of bug that costs the most hours. When two places can both mutate the same information they will eventually disagree, and the symptom surfaces nowhere near the cause, so you end up reading four files trying to understand why the name in the header does not match the name on the profile underneath it. The part about snapshots covers the version I have seen most often, which is holding onto the original record to compare against the edited one for a dirty indicator. That original is a reference. The moment it becomes state you have the second mutable copy the rule exists to stop.

Rule three is derived state, and it is the clearest example I know of the gap between a model knowing something and a model doing it. Ask any of them about storing derived state and you get a correct, well organised answer about single sources of truth. Then in the middle of building a component that fetches a list, filters it, and renders a summary, out comes a `total` in useState with an effect keeping it in sync with `items`, because at that point in the generation storing the number is simply the cheapest available move. The rule teaches the model nothing it did not already know. What it does is close the door on the cheap move.

Rule four goes after the same problem from the mechanism side. An effect that only reads state and writes state has no reason to run. It fires on renders that did not need it, it opens a window where the value is one tick behind, and looked at on its own during review it seems completely fine, which is exactly why it survives review.

Rule five is smaller than the others and I nearly left it out. Inferring loading from a null check works right until null becomes a legitimate value in your data, or until somebody asks you to tell an empty result apart from a failed request. Then you add a second flag and now two things describe one situation. Putting the state into the type means the component cannot quietly forget a case, because it will not compile.

Rule six is about forms because forms are the first place the separation between presentation and logic falls apart. A form that owns its initial values and does its own transformation cannot be reused for the edit case, cannot be tested without mounting it, and over time turns into the file where business logic goes to hide. Keeping the transformation in the hook is what keeps a form a form.

There was a rule about prefixing boolean state with `is` or `has`. Perfectly good rule. It belongs in the naming section, and once it was in two places I could see exactly how the two copies were going to drift apart over the next year.

There was also a longer one about reaching for useReducer instead of several useState calls. Every version I wrote either repeated something rule one already covered or leaned on a threshold I could not actually defend when someone asked me why that number. It never once changed an output, so it went.

Take a component the AI generated recently with no rules in place. Paste these six in front of a prompt describing the same feature and generate it again.

Then look specifically at what the rules cover. Where did the state land. Is anything stored that could have been computed. Are there two places holding the same information. Does the loading state come from a type or from a null check.

If the second version looks basically the same as the first, the rules are not specific enough for your codebase yet, and the fix is sharpening the conditions rather than adding more rules on top. Six rules with edges beat twenty vague ones, and the vague ones cost you attention on the way to the useful ones.

The next part covers architecture and file boundaries, which turns out to decide how much these state rules can accomplish in the first place.
