Skip to main content

Managing Side Effects

Elf is a state management solution, and it doesn't force you to manage side effects in a certain way. But the same team also created companion packages that can be used with Elf to handle side effects.

A side-effect is anything that happens outside of the normal flow of the store—interacting with the API asynchronously, setting intervals and timeouts, updating the local storage, etc.

It's entirely up to the developer to model and implement those tasks and update the store.

Let's examine three ways to handle side effects in our application:

Using Services

In most cases, services are the most straightforward solution:

todos.service.ts
import { setTodos, addTodo } from './todos.repository';
import { tap } from 'rxjs/operators';

export function fetchTodos() {
return http.get('todos').pipe(
tap(setTodos)
)
}

export function addTodo(todo: Todo) {
return http.post('todos', todo).pipe(
tap(addTodo)
)
}

And subscribe in the component. Below is an example using a React component:

import { useObservable } from '@ngneat/react-rxjs';
import { todos$ } from './todos.repository';
import { fetchTodos } from './todos.service';

function Todos() {
const [todos] = useObservable(todos$);

useEffect(() => {
fetchTodos().subscribe();
}, [])

return <div>{todos}</div>
}

Check out the @ngneat/react-rxjs library for more information.

Using Effects

We can register effects that'll execute when we dispatch actions using @ngneat/effects.

todos.effects.ts

import { createAction, createEffect, ofType } from '@ngneat/effects';

const loadTodos = createAction('[Todos] Load');

export const loadTodos$ = createEffect(actions =>
actions.pipe(
ofType(loadTodos),
switchMap((todo) => todosApi.loadTodos()),
tap(setTodos)
)
);

Below is an example using a React component:

import { useEffects } from '@ngneat/effects-hook';
import { dispatch } from '@ngneat/effects';
import { useObservable } from '@ngneat/react-rxjs';
import { useEffect } from 'react';

export function TodosPage() {
const [todos] = useObservable(todos$);

useEffects([loadTodos$]);

useEffect(() => dispatch(loadTodos()), []);

return {todos}
}

In the official documentation, you can find more information and an Angular example.

Using Effect Functions

You may prefer effect functions if you're not a big fan of actions.

todos.effects.ts
import { createEffectFn } from '@ngneat/effects';

export const searchTodoEffect = createEffectFn((searchTerm$: Observable<string>) => {
return searchTerm$.pipe(
debounceTime(300),
switchMap((searchTerm) => fetchTodos({ searchTerm })),
tap(setTodos)
);
});

Below is an example using a React component:

import { useEffectFn } from '@ngneat/effects-hooks';

function SearchComponent() {
const searchTodo = useEffectFn(searchTodoEffect);

return <input onChange = {({ target: { value } }) => searchTodo(value) }/>
}

In the official documentation, you can find more information and an Angular example. It's possible to use effects and effect functions simultaneously if you like.