-
Notifications
You must be signed in to change notification settings - Fork 167
Fix Node 20 lint violations and React hook warnings #1240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: hortison <160366376+hortison@users.noreply.github.com>
Co-authored-by: hortison <160366376+hortison@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes Node 20 lint violations and React hook warnings by addressing missing import rules, unsafe ref access, render-time component creation, setState-in-effect patterns, and unused-expression issues.
Changes:
- Removed unnecessary eslint disable comments and improved type safety in tests
- Simplified worker cleanup and callback invocation with optional chaining
- Fixed ref forwarding in Hidden component to avoid accessing refs during render
- Removed setState-in-effect patterns by deriving open state from data
- Inlined icon/SVG selection to prevent component creation during render
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| ResponsiveDataTable.test.tsx | Removed unnecessary eslint disable comment for import/first rule |
| routing.test.ts | Replaced any cast with more specific unknown as RESOURCE_TYPE cast |
| workerfy.ts | Added optional chaining for worker cleanup method calls |
| Hidden.tsx | Fixed ref forwarding by passing ref directly to MuiHidden |
| CatalogDesignTable.tsx | Used optional chaining for setSearch callback invocation |
| ChallengesSection.tsx | Derived isOpen state from data to avoid setState-in-effect |
| LearningSection.tsx | Derived isOpen state from data to avoid setState-in-effect |
| ContentClassInfo.tsx | Inlined icon rendering to avoid creating components during render |
| CatalogCardDesignLogo.tsx | Inlined SVG selection logic instead of creating component |
| FlipCard.tsx | Used optional chaining for onClick and onShow callbacks |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const toggleOpenLearning = (): void => { | ||
| setOpenLearning((prev) => !prev); | ||
| setAutoUpdate(false); | ||
| setOpenLearning(!isOpen); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The toggle logic has a race condition. When toggling, setOpenLearning(!isOpen) uses the value of isOpen which is derived from autoUpdate and other state. If autoUpdate is true when the function runs, isOpen will be hasLearning, not openLearning. This means the toggle won't work correctly after the first click. The fix should read the previous state value: setOpenLearning((prev) => !prev) to ensure proper toggle behavior.
| setOpenLearning(!isOpen); | |
| setOpenLearning((prev) => !prev); |
| const toggleOpenChallenges = () => { | ||
| setOpenChallenges((prev) => !prev); | ||
| setAutoUpdate(false); | ||
| setOpenChallenges(!isOpen); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The toggle logic has a race condition. When toggling, setOpenChallenges(!isOpen) uses the value of isOpen which is derived from autoUpdate and other state. If autoUpdate is true when the function runs, isOpen will be hasChallenges, not openChallenges. This means the toggle won't work correctly after the first click. The fix should read the previous state value: setOpenChallenges((prev) => !prev) to ensure proper toggle behavior.
| setOpenChallenges(!isOpen); | |
| setOpenChallenges((prev) => !prev); |
| if (event.data.type === WORKER_COMMANDS.STOP_ACTOR) { | ||
| snapshotSubscription?.unsubscribe && snapshotSubscription.unsubscribe(); | ||
| actorRef?.stop && actorRef.stop(); | ||
| snapshotSubscription?.unsubscribe?.(); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The double optional chaining ?.unsubscribe?.() is unnecessary. The Subscription type from xstate guarantees that unsubscribe is a method on the object. Since snapshotSubscription can be null, only the first optional chaining is needed: snapshotSubscription?.unsubscribe(). The second optional chaining adds no value and suggests the method might not exist, which is not the case.
| snapshotSubscription?.unsubscribe?.(); | |
| snapshotSubscription?.unsubscribe(); |
| snapshotSubscription?.unsubscribe && snapshotSubscription.unsubscribe(); | ||
| actorRef?.stop && actorRef.stop(); | ||
| snapshotSubscription?.unsubscribe?.(); | ||
| actorRef?.stop?.(); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The double optional chaining ?.stop?.() is unnecessary. The AnyActorRef type from xstate guarantees that stop is a method on the object. Since actorRef can be null, only the first optional chaining is needed: actorRef?.stop(). The second optional chaining adds no value and suggests the method might not exist, which is not the case.
| actorRef?.stop?.(); | |
| actorRef?.stop(); |
Node 20 linting surfaced missing import rule config, unsafe ref access during render, render-time component creation, setState-in-effect patterns, and unused-expression issues across tests and components.
ResponsiveDataTable.test.tsxand replacedanycast in routing tests with typedRESOURCE_TYPE.MuiHiddento avoid accessing refs during render.setSearch?.(...).Example:
Original prompt
Node 20 lint checks. Fix these lint errors. See https://github.com/layer5io/sistent/actions/runs/21158045193/job/60849181565?pr=1239
/home/runner/work/sistent/sistent/src/testing/ResponsiveDataTable.test.tsx
Error: 53:1 error Definition for rule 'import/first' was not found import/first
/home/runner/work/sistent/sistent/src/testing/routing.test.ts
Error: 46:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
/home/runner/work/sistent/sistent/src/actors/worker/workerfy.ts
Error: 74:7 error Expected an assignment or function call and instead saw an expression @typescript-eslint/no-unused-expressions
Error: 75:7 error Expected an assignment or function call and instead saw an expression @typescript-eslint/no-unused-expressions
/home/runner/work/sistent/sistent/src/base/Hidden/Hidden.tsx
5:55 error Error: Cannot access refs during render
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the
currentproperty) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)./home/runner/work/sistent/sistent/src/base/Hidden/Hidden.tsx:5:55
3 |
4 | export const Hidden = React.forwardRef<HTMLDivElement, MuiHiddenProps>((props, ref) => {
/home/runner/work/sistent/sistent/src/custom/CatalogDesignTable/CatalogDesignTable.tsx
Error: 75:11 error Expected an assignment or function call and instead saw an expression @typescript-eslint/no-unused-expressions
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/ChallengesSection.tsx
22:7 error Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/ChallengesSection.tsx:22:7
20 | useEffect(() => {
21 | if (autoUpdate) {
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/ContentClassInfo.tsx
64:10 error Error: Cannot create components during render
Components created during render will reset their state each time they are created. Declare components outside of render.
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/ContentClassInfo.tsx:64:10
62 | }}
63 | >
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/ContentClassInfo.tsx:38:54
36 | } as const;
37 |
/home/runner/work/sistent/sistent/src/custom/CatalogDetail/LearningSection.tsx
22:7 error Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.