Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Effector
https://github.com/effector/effector
Admin
Effector is a JavaScript framework for managing business logic and data flow in applications,
...
Tokens:
187,009
Snippets:
1,676
Trust Score:
9.2
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
75.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Effector Effector is a reactive state management library for JavaScript applications (React, React Native, Vue, Svelte, Solid, Node.js). It provides a declarative approach to managing application state through events, stores, and effects. The library follows core principles of lightweight stores, free store composition, and predictable API design without requiring decorators, classes, or proxies. The core functionality centers around three main unit types: Events (for triggering state changes), Stores (for holding state), and Effects (for handling side effects like API calls). Effector provides powerful operators like `sample`, `combine`, `attach`, `split`, and `merge` for composing these units. It also includes a Fork API for server-side rendering (SSR) and testing, allowing isolated scopes of application state. ## createEvent Creates an event that can be called to trigger state changes. Events are the primary way to initiate data flow in effector applications. ```javascript import { createEvent, createStore } from "effector"; const addNumber = createEvent(); const resetCounter = createEvent(); const $counter = createStore(0) .on(addNumber, (state, number) => state + number) .reset(resetCounter); $counter.watch((state) => { console.log("counter:", state); }); // => counter: 0 addNumber(10); // => counter: 10 addNumber(5); // => counter: 15 resetCounter(); // => counter: 0 // Derived events with .map() const extractPartOfArray = createEvent(); const slicedArray = extractPartOfArray.map((arr) => arr.slice(2)); slicedArray.watch((part) => console.log(part)); extractPartOfArray([1, 2, 3, 4, 5, 6]); // => [3, 4, 5, 6] ``` ## createStore Creates a store to hold application state. Stores can react to events and effects, and can be combined with other stores. ```javascript import { createEvent, createStore } from "effector"; const addTodo = createEvent(); const toggleTodo = createEvent(); const clearTodos = createEvent(); const $todos = createStore([]) .on(addTodo, (todos, newTodo) => [...todos, { text: newTodo, done: false }]) .on(toggleTodo, (todos, index) => todos.map((todo, i) => (i === index ? { ...todo, done: !todo.done } : todo)) ) .reset(clearTodos); // Derived store using .map() const $completedTodos = $todos.map((todos) => todos.filter((todo) => todo.done)); const $todoCount = $todos.map((todos) => todos.length); $todos.watch((todos) => console.log("todos:", todos)); $completedTodos.watch((completed) => console.log("completed:", completed.length)); addTodo("Learn effector"); // => todos: [{ text: 'Learn effector', done: false }] // => completed: 0 toggleTodo(0); // => todos: [{ text: 'Learn effector', done: true }] // => completed: 1 ``` ## createEffect Creates an effect for handling asynchronous operations and side effects. Effects have built-in pending state, done/fail events, and automatic error handling. ```javascript import { createStore, createEffect } from "effector"; const fetchUserFx = createEffect(async (userId) => { const response = await fetch(`https://api.example.com/users/${userId}`); if (!response.ok) throw new Error("User not found"); return response.json(); }); const $user = createStore(null); const $error = createStore(null); const $loading = fetchUserFx.pending; $user.on(fetchUserFx.doneData, (_, user) => user); $error.on(fetchUserFx.failData, (_, error) => error.message); $error.reset(fetchUserFx); // Watch effect lifecycle fetchUserFx.pending.watch((pending) => { console.log(`Loading: ${pending}`); }); fetchUserFx.done.watch(({ params, result }) => { console.log(`Fetched user ${params}:`, result); }); fetchUserFx.fail.watch(({ params, error }) => { console.error(`Failed to fetch user ${params}:`, error); }); await fetchUserFx(123); // => Loading: true // => Fetched user 123: { id: 123, name: "John" } // => Loading: false ``` ## sample Connects units together by taking data from a source when a clock triggers. The most powerful operator for building reactive data flows. ```javascript import { createEvent, createStore, createEffect, sample } from "effector"; const submitForm = createEvent(); const $formData = createStore({ email: "", password: "" }); const $isValid = createStore(false); const loginFx = createEffect(async (data) => { const response = await fetch("/api/login", { method: "POST", body: JSON.stringify(data), }); return response.json(); }); // Basic sample: when submitForm triggers, take $formData and send to loginFx sample({ clock: submitForm, source: $formData, target: loginFx, }); // With filter: only proceed if form is valid sample({ clock: submitForm, source: $formData, filter: $isValid, target: loginFx, }); // With transformation function sample({ clock: submitForm, source: $formData, fn: (formData, _clockPayload) => ({ ...formData, timestamp: Date.now(), }), target: loginFx, }); // Without target - returns a new unit const $lastSubmittedData = sample({ clock: submitForm, source: $formData, }); ``` ## combine Combines multiple stores into a single derived store. Updates automatically when any source store changes. ```javascript import { createStore, combine } from "effector"; const $firstName = createStore("John"); const $lastName = createStore("Doe"); const $age = createStore(30); // Object form - creates store with object shape const $user = combine({ firstName: $firstName, lastName: $lastName, age: $age, }); // => { firstName: "John", lastName: "Doe", age: 30 } // With transformation function const $fullName = combine($firstName, $lastName, (first, last) => `${first} ${last}`); // => "John Doe" // Array form const $userData = combine([$firstName, $lastName, $age]); // => ["John", "Doe", 30] // Practical example: form validation const $email = createStore(""); const $password = createStore(""); const $formValidation = combine({ email: $email, password: $password }, ({ email, password }) => { const errors = []; if (!email.includes("@")) errors.push("Invalid email"); if (password.length < 8) errors.push("Password too short"); return { isValid: errors.length === 0, errors }; }); ``` ## attach Creates a new effect based on an existing one, with the ability to access store data. Useful for reusing effect logic with different contexts. ```javascript import { createStore, createEffect, attach } from "effector"; const $authToken = createStore("token123"); const baseFetchFx = createEffect(async ({ url, token, data }) => { const response = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(data), }); return response.json(); }); // Create specialized effects that automatically include auth token const createUserFx = attach({ source: $authToken, effect: baseFetchFx, mapParams: (userData, token) => ({ url: "/api/users", token, data: userData, }), }); const updateUserFx = attach({ source: $authToken, effect: baseFetchFx, mapParams: ({ id, ...data }, token) => ({ url: `/api/users/${id}`, token, data, }), }); // Usage - token is automatically included await createUserFx({ name: "Alice", email: "alice@example.com" }); await updateUserFx({ id: 1, name: "Alice Updated" }); ``` ## split Routes events to different targets based on conditions. Works like pattern matching for event payloads. ```javascript import { createEvent, createEffect, split } from "effector"; const messageReceived = createEvent(); const showTextPopup = createEvent(); const playAudio = createEvent(); const showImage = createEvent(); const reportUnknownFx = createEffect((msg) => console.log("Unknown:", msg.type)); split({ source: messageReceived, match: { text: (msg) => msg.type === "text", audio: (msg) => msg.type === "audio", image: (msg) => msg.type === "image", }, cases: { text: showTextPopup, audio: playAudio, image: showImage, __: reportUnknownFx, // default case }, }); showTextPopup.watch(({ content }) => console.log("Text:", content)); playAudio.watch(({ url }) => console.log("Audio:", url)); messageReceived({ type: "text", content: "Hello!" }); // => Text: Hello! messageReceived({ type: "audio", url: "song.mp3" }); // => Audio: song.mp3 messageReceived({ type: "unknown", data: {} }); // => Unknown: unknown ``` ## merge Combines multiple units into a single event that fires when any of them triggers. ```javascript import { createEvent, createStore, merge } from "effector"; const buttonClicked = createEvent(); const formSubmitted = createEvent(); const keyPressed = createEvent(); // Merge multiple events const userInteracted = merge([buttonClicked, formSubmitted, keyPressed]); userInteracted.watch((payload) => { console.log("User interaction:", payload); }); buttonClicked("save"); // => User interaction: save formSubmitted({ email: "test@example.com" }); // => User interaction: { email: "test@example.com" } // Merge stores - fires on any store update const $page = createStore(1); const $filter = createStore("all"); const settingsChanged = merge([$page, $filter]); settingsChanged.watch((value) => console.log("Setting changed to:", value)); ``` ## fork and allSettled Creates an isolated scope for SSR and testing. Each scope has independent store values and effect handlers. ```javascript import { createStore, createEvent, createEffect, fork, allSettled, serialize } from "effector"; const increment = createEvent(); const fetchDataFx = createEffect(async () => { const res = await fetch("/api/data"); return res.json(); }); const $counter = createStore(0).on(increment, (n) => n + 1); const $data = createStore(null).on(fetchDataFx.doneData, (_, data) => data); // Create isolated scope for SSR const scope = fork({ values: [[$counter, 10]], // preset store values handlers: [[fetchDataFx, async () => ({ serverData: true })]], // mock handlers }); // Run effects and wait for completion await allSettled(increment, { scope }); await allSettled(fetchDataFx, { scope }); // Read scoped state console.log(scope.getState($counter)); // => 11 console.log(scope.getState($data)); // => { serverData: true } // Global state unchanged console.log($counter.getState()); // => 0 // Serialize for hydration const serialized = serialize(scope); // => { "counterId": 11, "dataId": { serverData: true } } ``` ## useUnit (React) React hook that binds stores, events, and effects to the current scope and returns their values. ```jsx import { createStore, createEvent, createEffect, fork } from "effector"; import { useUnit, Provider } from "effector-react"; const increment = createEvent(); const decrement = createEvent(); const fetchUserFx = createEffect(async (id) => { const res = await fetch(`/api/users/${id}`); return res.json(); }); const $count = createStore(0) .on(increment, (n) => n + 1) .on(decrement, (n) => n - 1); const $user = createStore(null).on(fetchUserFx.doneData, (_, user) => user); const $loading = fetchUserFx.pending; function Counter() { // Bind store and events const { count, onIncrement, onDecrement } = useUnit({ count: $count, onIncrement: increment, onDecrement: decrement, }); return ( <div> <p>Count: {count}</p> <button onClick={() => onIncrement()}>+</button> <button onClick={() => onDecrement()}>-</button> </div> ); } function UserProfile({ userId }) { const [user, loading, fetchUser] = useUnit([$user, $loading, fetchUserFx]); useEffect(() => { fetchUser(userId); }, [userId]); if (loading) return <p>Loading...</p>; return <p>User: {user?.name}</p>; } // Wrap app with Provider for SSR const scope = fork(); function App() { return ( <Provider value={scope}> <Counter /> <UserProfile userId={1} /> </Provider> ); } ``` ## useList (React) Efficiently renders list stores with automatic memoization of items. ```jsx import { createStore, createEvent } from "effector"; import { useList, useUnit } from "effector-react"; const addItem = createEvent(); const toggleItem = createEvent(); const removeItem = createEvent(); const $items = createStore([ { id: 1, text: "Learn effector", done: false }, { id: 2, text: "Build app", done: false }, ]) .on(addItem, (items, text) => [...items, { id: Date.now(), text, done: false }]) .on(toggleItem, (items, id) => items.map((item) => (item.id === id ? { ...item, done: !item.done } : item)) ) .on(removeItem, (items, id) => items.filter((item) => item.id !== id)); function TodoList() { const [onToggle, onRemove] = useUnit([toggleItem, removeItem]); return useList($items, { keys: [], // dependencies for re-render getKey: (item) => item.id, // custom key function fn: (item) => ( <li> <input type="checkbox" checked={item.done} onChange={() => onToggle(item.id)} /> <span style={{ textDecoration: item.done ? "line-through" : "none" }}>{item.text}</span> <button onClick={() => onRemove(item.id)}>Remove</button> </li> ), placeholder: <p>No items yet</p>, }); } ``` ## createGate (React) Creates a gate component for lifecycle management - tracks when a component mounts/unmounts and its props. ```jsx import { createGate } from "effector-react"; import { createEffect, sample } from "effector"; const PageGate = createGate({ defaultState: { id: null } }); const fetchPageDataFx = createEffect(async (id) => { const res = await fetch(`/api/pages/${id}`); return res.json(); }); // Fetch data when gate opens (component mounts) sample({ clock: PageGate.open, fn: ({ id }) => id, target: fetchPageDataFx, }); // Track gate state PageGate.status.watch((isOpen) => console.log("Page mounted:", isOpen)); PageGate.state.watch((props) => console.log("Gate props:", props)); function Page({ pageId }) { return ( <div> <PageGate id={pageId} /> <h1>Page Content</h1> </div> ); } ``` ## useUnit (Vue) Vue Composition API hook for binding stores, events, and effects. ```vue <script setup> import { useUnit } from "effector-vue/composition"; import { createStore, createEvent } from "effector"; const increment = createEvent(); const decrement = createEvent(); const $count = createStore(0) .on(increment, (n) => n + 1) .on(decrement, (n) => n - 1); // Object form const { count, onIncrement, onDecrement } = useUnit({ count: $count, onIncrement: increment, onDecrement: decrement, }); </script> <template> <div> <p>Count: {{ count }}</p> <button @click="onIncrement">+</button> <button @click="onDecrement">-</button> </div> </template> ``` ## Summary Effector excels in complex applications requiring predictable state management with fine-grained reactivity. Key use cases include: form state management with real-time validation, API data fetching with loading/error states, multi-step workflows with conditional logic, and server-side rendering with state hydration. The Fork API makes testing straightforward by allowing complete isolation of state and effect mocking. Integration patterns typically involve creating a model layer separate from UI components. Events define user intentions, stores hold computed state, and effects handle external interactions. Components connect to this model using framework-specific hooks (`useUnit` for React/Vue, direct store subscriptions for Svelte). For SSR, create a scope per request with `fork`, populate state with `allSettled`, serialize with `serialize`, and hydrate on the client. This architecture promotes separation of concerns and makes business logic highly testable and reusable across different UI frameworks.