Skip to content

Commit

Permalink
Resore an article handle_events_in_ui_frameworks
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Aug 23, 2024
1 parent 5231825 commit f5c1944
Showing 1 changed file with 74 additions and 66 deletions.
140 changes: 74 additions & 66 deletions apps/website/docs/magazine/handle_events_in_ui_frameworks.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,75 +55,11 @@ Our application calls it every time when a request for data is failed, and we ne

We need to bound `dataLoadingFailed` and `notification.useNotification` somehow.

Let us take a look on a couple of solutions and find the best one.

### 🔴 Global `notification` service

Ant Design allows using global notification instance.

```ts{7-17}
// model.ts
import { createEvent, createEffect, sample } from 'effector';
import { notification } from 'antd';
const dataLoadingFailed = createEvent<{ reason: string }>();
// Create an Effect to show a notification
const showWarningFx = createEffect((params: { message: string }) => {
notification.warning(params);
});
// Execute it when dataLoadingFailed is happened
sample({
clock: dataLoadingFailed,
fn: ({ reason }) => ({ message: reason }),
target: showWarningFx,
});
```

In this solution it is not possible to use any Ant's settings from React Context, because it does not have access to the React at all. It means that notifications will not be styled properly and could look different from the rest of the application.

So, this is not a solution.

### 🔴 Just `.watch` an [_Event_](https://effector.dev/en/api/effector/event/) in a component

It is possible to call `.watch`-method of an [_Event_](https://effector.dev/en/api/effector/event/) in a component.

```tsx{9-17}
import { useEffect } from 'react';
import { notification } from 'antd';
import { dataLoadingFailed } from './model';
function App() {
const [api, contextHolder] = notification.useNotification();
useEffect(
() =>
dataLoadingFailed.watch(({ reason }) => {
api.warning({
message: reason,
});
}),
[api]
);
return (
<>
{contextHolder}
{/* ...the rest of the application */}
</>
);
}
```

In this solution we do not respect [Fork API rules](/magazine/fork_api_rules), it means that we could have memory leaks, problems with test environments and Storybook-like tools.

So, this is not a solution.
Let us take a look on a ideal solution and a couple of not-so-good solutions.

### 🟢 Save `notification` instance to a [_Store_](https://effector.dev/docs/api/effector/store)

We can combine the previous two solutions and save `notification` API-instance to a [_Store_](https://effector.dev/docs/api/effector/store). Let us create a couple new units to do it.
The best way is saving `notification` API-instance to a [_Store_](https://effector.dev/docs/api/effector/store) and using it thru [_Effect_](https://effector.dev/docs/api/effector/effect). Let us create a couple new units to do it.

```ts
// notifications.ts
Expand Down Expand Up @@ -210,6 +146,78 @@ sample({
});
```

Now we have a valid solution to handle [_Events_](https://effector.dev/en/api/effector/event/) on UI-layer without exposing the whole data-flow.

However, if you want to know why other (maybe more obvious) solutions are not so good, you can read about them below 👇

::: details Not-so-good solutions

### 🔴 Global `notification` service

Ant Design allows using global notification instance.

```ts{7-17}
// model.ts
import { createEvent, createEffect, sample } from 'effector';
import { notification } from 'antd';
const dataLoadingFailed = createEvent<{ reason: string }>();
// Create an Effect to show a notification
const showWarningFx = createEffect((params: { message: string }) => {
notification.warning(params);
});
// Execute it when dataLoadingFailed is happened
sample({
clock: dataLoadingFailed,
fn: ({ reason }) => ({ message: reason }),
target: showWarningFx,
});
```

In this solution it is not possible to use any Ant's settings from React Context, because it does not have access to the React at all. It means that notifications will not be styled properly and could look different from the rest of the application.

So, this is not a solution.

### 🔴 Just `.watch` an [_Event_](https://effector.dev/en/api/effector/event/) in a component

It is possible to call `.watch`-method of an [_Event_](https://effector.dev/en/api/effector/event/) in a component.

```tsx{9-17}
import { useEffect } from 'react';
import { notification } from 'antd';
import { dataLoadingFailed } from './model';
function App() {
const [api, contextHolder] = notification.useNotification();
useEffect(
() =>
dataLoadingFailed.watch(({ reason }) => {
api.warning({
message: reason,
});
}),
[api]
);
return (
<>
{contextHolder}
{/* ...the rest of the application */}
</>
);
}
```

In this solution we do not respect [Fork API rules](/magazine/fork_api_rules), it means that we could have memory leaks, problems with test environments and Storybook-like tools.

So, this is not a solution.

:::

## Summary

To bind some UI-framework specific API to Effector's data-flow we need to follow these steps:
Expand Down

0 comments on commit f5c1944

Please sign in to comment.