Skip to content

Commit 337f0c2

Browse files
authored
Merge pull request feature-sliced#378 from feature-sliced/bugfix/EDITORSHIP-377-critical-mistakes-with-effector
EDITORSHIP-377: Fix critical mistakes with effector
2 parents c1c020e + 0a3ac5c commit 337f0c2

File tree

8 files changed

+161
-117
lines changed

8 files changed

+161
-117
lines changed

website/i18n/en/docusaurus-plugin-content-docs/current/concepts/low-coupling.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -103,25 +103,25 @@ The page data model will be organized as a **composition of features and entitie
103103
> However, the implementation in the form of a factory is optional - the feature may depend on the underlying layers and directly
104104
105105
```ts title=pages/main/model.ts
106-
import { userStore } from "entitites/user"
107-
import { conversationStore } from "entities/conversation"
108-
import { contactStore } from "entities/contact"
106+
import { userModel } from "entitites/user"
107+
import { conversationModel } from "entities/conversation"
108+
import { contactModel } from "entities/contact"
109109

110110
import { createMessageInput } from "features/message-input"
111111
import { createConversationSwitch } from "features/conversation-switch"
112112

113113
import { beautifiy } from "shared/lib/beautify-text"
114114

115115
export const { allConversations, setConversation } = createConversationSwitch({
116-
contacts: contactStore.allContacts,
117-
setConversation: conversationStore.setConversation,
118-
currentConversation: conversationStore.conversation,
119-
currentUser: userStore.currentUser
116+
contacts: contactModel.allContacts,
117+
setConversation: conversationModel.setConversation,
118+
currentConversation: conversationModel.conversation,
119+
currentUser: userModel.currentUser
120120
})
121121

122122
export const { sendMessage, attachFile } = createMessageInput({
123-
author: userStore.currentUser
124-
send: conversationStore.sendMessage,
123+
author: userModel.currentUser
124+
send: conversationModel.sendMessage,
125125
formatMessage: beautify
126126
})
127127
```

website/i18n/en/docusaurus-plugin-content-docs/current/concepts/public-api.md

+15-14
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ The public API must **control access** to the contents of the module
6464
+ import { AuthForm } from "features/auth-form"
6565
```
6666

67-
### 2. Anti-fragility
67+
### 2. Sustainability for changes
6868

69-
The public API should be **anti-fragile** - resistant to changes inside the module
69+
The public API should be sustainable for changes inside the module
7070

7171
- Breaking changes in the behavior of the module are reflected in the change of the Public API
7272

@@ -102,34 +102,34 @@ The public API should facilitate **easy and flexible integration**
102102

103103
```ts title=features/auth-form/index.ts
104104
export { Form } from "./ui"
105-
export * as store from "./model"
105+
export * as model from "./model"
106106
```
107107

108108
```ts title=features/post-form/index.ts
109109
export { Form } from "./ui"
110-
export * as store from "./model"
110+
export * as model from "./model"
111111
```
112112

113113
```diff
114-
- import { Form, store } from "features/auth-form"
115-
- import { Form, store } from "features/post-form"
114+
- import { Form, model } from "features/auth-form"
115+
- import { Form, model } from "features/post-form"
116116
```
117117

118118
- **Good:** the collision is solved at the interface level
119119

120120
```ts title=features/auth-form/index.ts
121121
export { Form as AuthForm } from "./ui"
122-
export * as authFormStore from "./model"
122+
export * as authFormModel from "./model"
123123
```
124124

125125
```ts title=features/post-form/index.ts
126126
export { Form as PostForm } from "./ui"
127-
export * as postFormStore from "./model"
127+
export * as postFormModel from "./model"
128128
```
129129

130130
```diff
131-
+ import { AuthForm, authFormStore } from "features/auth-form"
132-
+ import { PostForm, postFormStore } from "features/post-form"
131+
+ import { AuthForm, authFormModel } from "features/auth-form"
132+
+ import { PostForm, postFormModel } from "features/post-form"
133133
```
134134

135135
##### Flexible use
@@ -144,8 +144,9 @@ The public API should facilitate **easy and flexible integration**
144144
- **Good:** the "user" of the feature gets access to the necessary things iteratively and flexibly
145145

146146
```diff
147-
+ import { authFormStore } from "features/auth-form"
148-
+ dispatch(authFormStore.actions.updateUserDetails(...))
147+
+ import { authFormModel } from "features/auth-form"
148+
+ dispatch(authFormModel.effects.updateUserDetails(...)) // redux
149+
+ authFormModel.updateUserDetailsFx(...) // effector
149150
```
150151

151152
##### Resolution of collisions
@@ -172,7 +173,7 @@ Name collisions should be resolved at the level of the public interface, not the
172173

173174
```ts title=features/auth-form/index.ts
174175
export { Form as AuthForm } from "./ui"
175-
export * as authFormStore from "./model"
176+
export * as authFormModel from "./model"
176177
```
177178

178179
```ts title=features/post-form/model.ts
@@ -181,7 +182,7 @@ Name collisions should be resolved at the level of the public interface, not the
181182

182183
```ts title=features/post-form/index.ts
183184
export { Form as PostForm } from "./ui"
184-
export * as postFormStore from "./model"
185+
export * as postFormModel from "./model"
185186
```
186187

187188
## About re-exports

website/i18n/en/docusaurus-plugin-content-docs/current/get-started/quick-start.md

+7-21
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ const TasksListPage = () => {
509509
* @remark is a bad practice in the effector world and is presented here-just for a visual demonstration
510510
* It is better to fetch via event.pageMounted or reflect
511511
*/
512-
useEffect(() => taskModel.effects.getTasksListFx(), []);
512+
useEffect(() => taskModel.getTasksListFx(), []);
513513

514514
return (
515515
<Layout className={styles.root}>
@@ -556,30 +556,16 @@ export const $tasks = createStore<Task[]>(...)
556556

557557

558558
// We make a hook to get involved in updates react
559+
// @see In the case of effector, using a hook is an extreme measure, since computed stores are more preferable
559560
export const useTask = (taskId: number): import("shared/api").Task | undefined => {
560-
return useStore($tasks)[taskId];
561+
return useStoreMap({
562+
store: $tasks,
563+
keys: [taskId],
564+
fn: (tasks, [id]) => tasks[id] ?? null
565+
});
561566
};
562567
```
563568

564-
:::tip
565-
566-
For a more convenient public API of models, you can export selectors/hooks/events, etc. as separate objects.
567-
568-
**But the main thing** is that this does not undermine [the anti-fragility][refs-public-api] of the module
569-
570-
```ts
571-
export const events = { toggleTask, setQueryConfig };
572-
export const effects = { getTaskByIdFx, getTasksListFx };
573-
export const selectors = { useTask };
574-
```
575-
576-
```ts
577-
const task = taskModel.selectors.useTask(taskId);
578-
taskModel.events.toggleTask(taskId)
579-
```
580-
581-
:::
582-
583569
#### (features) Checkbox for the task
584570

585571
```tsx title=features/toggle-task/ui.tsx

website/i18n/en/docusaurus-plugin-content-docs/current/guides/examples/auth.mdx

+49-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
77

88
# Auth
99

10+
import Tabs from '@theme/Tabs';
11+
import TabItem from '@theme/TabItem';
12+
1013
Every application has business logic related **with the current authorized user.**
1114

1215
> Usually such an entity is called `Viewer` / `Principle` / `Session` - but within the framework of the article, we will focus on `viewer`, but it all depends on your project and team
@@ -21,9 +24,10 @@ Let's look at them in more detail below with examples
2124

2225
*The methodology does not regulate this level of nesting in any way yet*
2326

24-
2. It should also be understood that the examples given below are abstract and synthetic, and are formed by the author's point of view and experience
27+
2. It should also be understood that the examples below are abstract and synthetic, and are formed to demonstrate only the concepts of the methodology
28+
29+
*FeatureSliced does not regulate the best practices of a particular data-fetcher or state-manager*
2530

26-
*The implementation in your projects may differ, the methodology, again, does not regulate these aspects in any way*
2731

2832
:::
2933

@@ -73,12 +77,33 @@ export { ViewerAvatar } from "./avatar";
7377
...
7478
```
7579

80+
<Tabs>
81+
<TabItem value="redux" label="Redux" default>
82+
83+
In redux, the [redux-ducks](https://github.com/erikra/ducks-modular-redux) approach is generally accepted when its units (selectors/actions/...) they lie side by side and are clearly decomposed
84+
85+
But explicit decomposition is not required
86+
7687
```ts title=entities/user/model/index.ts
7788
export * as selectors from "./selectors";
7889
export * as events from "./events";
7990
export * as stores from "./stores";
8091
...
8192
```
93+
</TabItem>
94+
<TabItem value="effector" label="Effector">
95+
96+
The effector model will most often consist of a single file - because it is customary to store all units side by side there
97+
98+
If the units in the model can be schematically divided into several submodels, then [you can explicitly do this](https://github.com/feature-sliced/examples/pull/1#discussion_r654841332) denote in the Public API
99+
100+
```ts title=entities/user/model/index.ts
101+
export * as submodel1 from "./submodel1"
102+
export * as submodel2 from "./submodel2"
103+
...
104+
```
105+
</TabItem>
106+
</Tabs>
82107

83108
```ts title=entities/user/index.ts
84109
export * from "./ui"
@@ -117,23 +142,33 @@ export const UserCard = ({ data, ... }: UserCardProps) => {
117142

118143
At this level, the entity of the current user is usually created, with the re-export of hooks/contracts/selectors for use by the overlying layers
119144

120-
```ts title=entities/user/model/stores.ts
121-
// effector
122-
export const $user = createStore(...);
123-
// redux (+ toolkit)
124-
export const userSlice = createSlice(...)
125-
```
145+
<Tabs>
146+
<TabItem value="redux" label="Redux" default>
126147

127-
```ts title=entities/user/model/selectors.ts
128-
// effector
129-
export const useViewer = () => {
130-
return useStore($user)
131-
}
132-
// redux (+ toolkit)
148+
```ts
149+
// entities/viewer/model/selectors.ts
133150
export const useViewer = () => {
134151
return useSelector((store) => store.entities.userSlice);
135152
}
153+
export const useAuth = () => {
154+
const viewer = useViewer();
155+
return !!viewer
156+
}
157+
// entities/viewer/model/store.ts
158+
export const userSlice = createSlice(...)
159+
```
160+
</TabItem>
161+
<TabItem value="effector" label="Effector">
162+
163+
```ts
164+
// entities/viewer/model/index.ts
165+
export const $viewer = createStore(...);
166+
export const $isAuth = $viewer.map((viewer) => !!viewer);
167+
// **/**/ui.tsx
168+
const viewer = useStore($viewer);
136169
```
170+
</TabItem>
171+
</Tabs>
137172

138173
Also, other logic can be implemented here
139174

website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/low-coupling.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -103,25 +103,25 @@ const List: Component<ListProps> = ({ Header, Items }) => (
103103
> Однако, реализация в виде фабрики необязательна - фича может зависеть от нижележащих слоев и напрямую
104104
105105
```ts title=pages/main/model.ts
106-
import { userStore } from "entitites/user"
107-
import { conversationStore } from "entities/conversation"
108-
import { contactStore } from "entities/contact"
106+
import { userModel } from "entitites/user"
107+
import { conversationModel } from "entities/conversation"
108+
import { contactModel } from "entities/contact"
109109

110110
import { createMessageInput } from "features/message-input"
111111
import { createConversationSwitch } from "features/conversation-switch"
112112

113113
import { beautifiy } from "shared/lib/beautify-text"
114114

115115
export const { allConversations, setConversation } = createConversationSwitch({
116-
contacts: contactStore.allContacts,
117-
setConversation: conversationStore.setConversation,
118-
currentConversation: conversationStore.conversation,
119-
currentUser: userStore.currentUser
116+
contacts: contactModel.allContacts,
117+
setConversation: conversationModel.setConversation,
118+
currentConversation: conversationModel.conversation,
119+
currentUser: userModel.currentUser
120120
})
121121

122122
export const { sendMessage, attachFile } = createMessageInput({
123-
author: userStore.currentUser
124-
send: conversationStore.sendMessage,
123+
author: userModel.currentUser
124+
send: conversationModel.sendMessage,
125125
formatMessage: beautify
126126
})
127127
```

website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/public-api.md

+16-15
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ Public API должен осуществлять **контроль доступ
6565
+ import { AuthForm } from "features/auth-form"
6666
```
6767

68-
### 2. Анти-хрупкость
68+
### 2. Устойчивость к изменениям
6969

70-
Public API должен быть **анти-хрупким** - устойчивым к изменениям внутри модуля
70+
Public API должен быть **устойчивым к изменениям** внутри модуля
7171

72-
- Ломающие изменения поведения модуля отражаются в изменении Public API
72+
- Изменения, ломающие поведения модуля, должны отражаться в изменении Public API
7373

7474
#### Примеры
7575

@@ -103,34 +103,34 @@ Public API должен способствовать **легкой и гибк
103103

104104
```ts title=features/auth-form/index.ts
105105
export { Form } from "./ui"
106-
export * as store from "./model"
106+
export * as model from "./model"
107107
```
108108

109109
```ts title=features/post-form/index.ts
110110
export { Form } from "./ui"
111-
export * as store from "./model"
111+
export * as model from "./model"
112112
```
113113

114114
```diff
115-
- import { Form, store } from "features/auth-form"
116-
- import { Form, store } from "features/post-form"
115+
- import { Form, model } from "features/auth-form"
116+
- import { Form, model } from "features/post-form"
117117
```
118118

119119
- **Хорошо:** коллизия решена на уровне интерфейса
120120

121121
```ts title=features/auth-form/index.ts
122122
export { Form as AuthForm } from "./ui"
123-
export * as authFormStore from "./model"
123+
export * as authFormModel from "./model"
124124
```
125125

126126
```ts title=features/post-form/index.ts
127127
export { Form as PostForm } from "./ui"
128-
export * as postFormStore from "./model"
128+
export * as postFormModel from "./model"
129129
```
130130

131131
```diff
132-
+ import { AuthForm, authFormStore } from "features/auth-form"
133-
+ import { PostForm, postFormStore } from "features/post-form"
132+
+ import { AuthForm, authFormModel } from "features/auth-form"
133+
+ import { PostForm, postFormModel } from "features/post-form"
134134
```
135135

136136
##### Гибкое использование
@@ -145,8 +145,9 @@ Public API должен способствовать **легкой и гибк
145145
- **Хорошо:** "пользователь" фичи получает доступ к нужным вещам итеративно и гибко
146146

147147
```diff
148-
+ import { authFormStore } from "features/auth-form"
149-
+ dispatch(authFormStore.actions.updateUserDetails(...))
148+
+ import { authFormModel } from "features/auth-form"
149+
+ dispatch(authFormModel.effects.updateUserDetails(...)) // redux
150+
+ authFormModel.updateUserDetailsFx(...) // effector
150151
```
151152

152153
##### Разрешение коллизий
@@ -173,7 +174,7 @@ Public API должен способствовать **легкой и гибк
173174

174175
```ts title=features/auth-form/index.ts
175176
export { Form as AuthForm } from "./ui"
176-
export * as authFormStore from "./model"
177+
export * as authFormModel from "./model"
177178
```
178179

179180
```ts title=features/post-form/model.ts
@@ -182,7 +183,7 @@ Public API должен способствовать **легкой и гибк
182183

183184
```ts title=features/post-form/index.ts
184185
export { Form as PostForm } from "./ui"
185-
export * as postFormStore from "./model"
186+
export * as postFormModel from "./model"
186187
```
187188

188189
## О реэкспортах

0 commit comments

Comments
 (0)