-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add rule mandatory-useEvent
#100
Merged
Merged
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
e12975d
Add `isReactComponent` utility
AlexandrHoroshih a93f134
Add glob to dev deps
AlexandrHoroshih cbc6a21
Add util to read all examples
AlexandrHoroshih b083baa
Add util to detect hooks
AlexandrHoroshih 57f6cfa
Setup mandatory-useEvent rule template with tests
AlexandrHoroshih 5a98e27
Consider hook for detection of react component
AlexandrHoroshih 1c918dd
Include *tsx examples to tsconfig
AlexandrHoroshih 66ec987
Fix test expectations
AlexandrHoroshih d37fc43
Implement mandatory-useEvent rule
AlexandrHoroshih fdec4ed
Allow setup custom list of hooks to check
AlexandrHoroshih f069ec8
Use common nodeTypeIs to check for effector hooks
AlexandrHoroshih 7014be2
Add test for effector-react/scope import
AlexandrHoroshih 03cbe23
Add mandatory-useEvent to public export
AlexandrHoroshih 20f1616
Add mandatory-useEvent to scope preset
AlexandrHoroshih f863462
Add mandatory-useEvent to main readme
AlexandrHoroshih 5107a0f
Update version and changelog
AlexandrHoroshih 0595cae
Fix recommended rules list
AlexandrHoroshih 5ecb2c7
Add test for nested events
AlexandrHoroshih 9e67e09
Add tests for alternative useEvent imports support
AlexandrHoroshih File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
module.exports = { | ||
rules: { | ||
"effector/strict-effect-handlers": "error", | ||
"effector/mandatory-useEvent": "error", | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
rules/mandatory-useEvent/examples/correct-array-shape-via-useEvent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react"; | ||
import { createEvent, createEffect } from "effector"; | ||
|
||
const clicked = createEvent(); | ||
const mounted = createEvent(); | ||
const unmounted = createEvent(); | ||
const fetchFx = createEffect(() => {}); | ||
|
||
const Button: React.FC = () => { | ||
const [clickedEvent, mountedEvent, unmountedEvent, fetch] = useEvent([ | ||
clicked, | ||
mounted, | ||
unmounted, | ||
fetchFx, | ||
]); | ||
|
||
React.useEffect(() => { | ||
mountedEvent(); | ||
fetch(); | ||
|
||
return () => { | ||
unmountedEvent(); | ||
}; | ||
}, []); | ||
|
||
return <button onClick={() => clickedEvent()}>click</button>; | ||
}; | ||
|
||
export { Button }; |
12 changes: 12 additions & 0 deletions
12
rules/mandatory-useEvent/examples/correct-effect-via-useEvent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react"; | ||
|
||
import { fetchFx } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
const clickedEffect = useEvent(fetchFx); | ||
|
||
return <button onClick={clickedEffect}>click</button>; | ||
}; | ||
|
||
export { Button }; |
15 changes: 15 additions & 0 deletions
15
rules/mandatory-useEvent/examples/correct-event-as-object-prop.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react"; | ||
|
||
import * as model from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
const clickedEvent = useEvent(model.clicked); | ||
const mounted = useEvent(model.deepNestedModel.context.outputs.mounted); | ||
|
||
React.useEffect(mounted, []); | ||
|
||
return <button onClick={clickedEvent}>click</button>; | ||
}; | ||
|
||
export { Button }; |
12 changes: 12 additions & 0 deletions
12
rules/mandatory-useEvent/examples/correct-event-via-useEvent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react"; | ||
|
||
import { clicked } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
const clickedEvent = useEvent(clicked); | ||
|
||
return <button onClick={clickedEvent}>click</button>; | ||
}; | ||
|
||
export { Button }; |
26 changes: 26 additions & 0 deletions
26
rules/mandatory-useEvent/examples/correct-object-shape-via-useEvent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react"; | ||
|
||
import { clicked, mounted, fetchFx, unmounted } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
const { clickedEvent, mountedEvent, unmountedEvent, fetch } = useEvent({ | ||
clickedEvent: clicked, | ||
mountedEvent: mounted, | ||
unmountedEvent: unmounted, | ||
fetch: fetchFx, | ||
}); | ||
|
||
React.useEffect(() => { | ||
mountedEvent(); | ||
fetch(); | ||
|
||
return () => { | ||
unmountedEvent(); | ||
}; | ||
}, []); | ||
|
||
return <button onClick={() => clickedEvent(true)}>click</button>; | ||
}; | ||
|
||
export { Button }; |
26 changes: 26 additions & 0 deletions
26
rules/mandatory-useEvent/examples/correct-scope-import.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from "react"; | ||
import { useEvent } from "effector-react/scope"; | ||
|
||
import { clicked, mounted, fetchFx, unmounted } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
const { clickedEvent, mountedEvent, unmountedEvent, fetch } = useEvent({ | ||
clickedEvent: clicked, | ||
mountedEvent: mounted, | ||
unmountedEvent: unmounted, | ||
fetch: fetchFx, | ||
}); | ||
|
||
React.useEffect(() => { | ||
mountedEvent(); | ||
fetch(); | ||
|
||
return () => { | ||
unmountedEvent(); | ||
}; | ||
}, []); | ||
|
||
return <button onClick={() => clickedEvent(true)}>click</button>; | ||
}; | ||
|
||
export { Button }; |
13 changes: 13 additions & 0 deletions
13
rules/mandatory-useEvent/examples/incorrect-direct-effect-call-in-hook.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from "react"; | ||
|
||
import { fetchFx } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
React.useEffect(() => { | ||
fetchFx(); | ||
}, []); | ||
|
||
return <button>click</button>; | ||
}; | ||
|
||
export { Button }; |
9 changes: 9 additions & 0 deletions
9
rules/mandatory-useEvent/examples/incorrect-direct-event-call-in-callback.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from "react"; | ||
|
||
import { clicked } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
return <button onClick={() => clicked(null)}>click</button>; | ||
}; | ||
|
||
export { Button }; |
13 changes: 13 additions & 0 deletions
13
rules/mandatory-useEvent/examples/incorrect-direct-event-call-in-hook-cleanup-callback.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from "react"; | ||
|
||
import { unmounted } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
React.useEffect(() => { | ||
return () => unmounted(); | ||
}, []); | ||
|
||
return <button>click</button>; | ||
}; | ||
|
||
export { Button }; |
13 changes: 13 additions & 0 deletions
13
rules/mandatory-useEvent/examples/incorrect-direct-event-call-in-hook.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from "react"; | ||
|
||
import { mounted } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
React.useEffect(() => { | ||
mounted(); | ||
}, []); | ||
|
||
return <button>click</button>; | ||
}; | ||
|
||
export { Button }; |
11 changes: 11 additions & 0 deletions
11
rules/mandatory-useEvent/examples/incorrect-event-as-hook-callback.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from "react"; | ||
|
||
import { mounted } from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
React.useEffect(mounted, []); | ||
|
||
return <button>click</button>; | ||
}; | ||
|
||
export { Button }; |
9 changes: 9 additions & 0 deletions
9
rules/mandatory-useEvent/examples/incorrect-event-as-prop.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from "react"; | ||
|
||
import {clicked} from "./model"; | ||
|
||
const Button: React.FC = () => { | ||
return <button onClick={clicked}>click</button>; | ||
}; | ||
|
||
export { Button }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { createEffect, createEvent } from "effector"; | ||
|
||
export const clicked = createEvent<unknown>(); | ||
export const mounted = createEvent(); | ||
export const unmounted = createEvent(); | ||
export const fetchFx = createEffect(() => {}); | ||
|
||
export const deepNestedModel = { | ||
context: { | ||
outputs: { | ||
mounted, | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const { createLinkToRule } = require("../../utils/create-link-to-rule"); | ||
const { isInsideReactComponent } = require("../../utils/react"); | ||
const { nodeTypeIs } = require("../../utils/node-type-is"); | ||
const { traverseParentByType } = require("../../utils/traverse-parent-by-type"); | ||
|
||
module.exports = { | ||
meta: { | ||
type: "problem", | ||
docs: { | ||
description: | ||
"Forbids `Event` and `Effect` usage without `useEvent` in React components.", | ||
category: "Quality", | ||
recommended: true, | ||
url: createLinkToRule("mandatory-useEvent"), | ||
}, | ||
messages: { | ||
useEventNeeded: | ||
"{{ unitName }} must be wrapped with `useEvent` from `effector-react` before usage inside React components", | ||
}, | ||
schema: [], | ||
}, | ||
create(context) { | ||
const parserServices = context.parserServices; | ||
|
||
// TypeScript-only rule, since units can be imported from anywhere | ||
if (parserServices.hasFullTypeInformation) { | ||
return { | ||
Identifier(node) { | ||
if (isInsideReactComponent(node)) { | ||
if ( | ||
nodeTypeIs.effect({ node, context }) || | ||
nodeTypeIs.event({ node, context }) | ||
) { | ||
if (!isInsideUseEventCall({ node, context })) { | ||
context.report({ | ||
node, | ||
messageId: "useEventNeeded", | ||
data: { | ||
unitName: node.name, | ||
}, | ||
}); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
} | ||
|
||
return {}; | ||
}, | ||
}; | ||
|
||
function isInsideUseEventCall({ node, context }) { | ||
const calleeParentNode = traverseParentByType(node.parent, "CallExpression"); | ||
|
||
if (!calleeParentNode?.callee) return false; | ||
|
||
return nodeTypeIs.effectorReactHook({ | ||
node: calleeParentNode.callee, | ||
context, | ||
hook: ["useEvent", "useUnit"], | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# effector/mandatory-useEvent | ||
|
||
Forbids `Event` and `Effect` usage without `useEvent` in React components. | ||
This ensures `Fork API` compatibility and allows to write isomorphic code for SSR apps. | ||
|
||
```tsx | ||
const increment = createEvent(); | ||
|
||
// 👍 Event usage is wrapped with `useEvent` | ||
const GoodButton = () => { | ||
const incrementEvent = useEvent(increment); | ||
|
||
return <button onClick={incrementEvent}>+</button>; | ||
}; | ||
|
||
// 👎 Event is not wrapped with `useEvent` - component is not suitable for isomorphic SSR app | ||
const BadButton = () => { | ||
return <button onClick={increment}>+</button>; | ||
}; | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🤓 personally, I would prefer early returns rather than 3 levels of nesting.