Skip to content

Observable store

Classes

ObservableStoreObservable

Functions

slice(store, options)Object

Creates a slice of the store with its own state and actions, namespaced to avoid conflicts.

store(initialState, [options])ObservableStore

This function creates a new instance of ObservableStore with the provided initial state and enhances it with localStorage support if enabled. The store's state will be automatically persisted to and loaded from localStorage, using the provided name as the key. The localStorage option enables this behavior and can be toggled off if persistence is not needed.

ObservableStore ⇐ Observable

Kind: global class
Extends: Observable

new ObservableStore()

This class is used to create a store that can be observed for changes. It supports registering actions and middleware, making it flexible for various use cases.

Example

// Creating a store with initial state and registering actions
const CartStore = cami.store({
  cartItems: [],
});

CartStore.register('add', (state, product) => {
  const cartItem = { ...product, cartItemId: Date.now() };
  state.cartItems.push(cartItem);
});

CartStore.register('remove', (state, product) => {
  state.cartItems = state.cartItems.filter(item => item.cartItemId !== product.cartItemId);
});

// Using middleware for logging
const loggerMiddleware = (context) => {
  console.log(`Action ${context.action} was dispatched with payload:`, context.payload);
};
CartStore.use(loggerMiddleware);

observableStore.dispatch(action, [payload])

Dispatches an action to update the store's state.

Kind: instance method of ObservableStore

Param Type Description
action string | function The action type (string) or action creator (function).
[payload] any The optional payload object to pass to the reducer.

Example

// Dispatching a simple action
store.dispatch('increment');

// Dispatching an action with payload
store.dispatch('addItem', { id: 1, name: 'New Item' });

ObservableStore.use(middleware)

This method registers a middleware function to be used with the store. Useful if you like redux-style middleware.

Kind: static method of ObservableStore

Param Type Description
middleware function The middleware function to use

Example

const loggerMiddleware = (context) => {
  console.log(`Action ${context.action} was dispatched with payload:`, context.payload);
};
CartStore.use(loggerMiddleware);

ObservableStore.getState() ⇒ Object

Retrieves the current state of the store. This method is crucial in asynchronous operations or event-driven environments to ensure the most current state is accessed, as the state might change frequently due to user interactions or other asynchronous updates.

Kind: static method of ObservableStore
Returns: Object - - The current state of the store.

ObservableStore.register(action, reducer)

This method registers a reducer function for a given action type. Useful if you like redux-style reducers.

Kind: static method of ObservableStore
Throws:

  • Error - Throws an error if the action type is already registered
Param Type Description
action string The action type
reducer function The reducer function for the action

Example

// Creating a store with initial state and registering actions
const CartStore = cami.store({
  cartItems: [],
});

CartStore.register('add', (state, product) => {
  const cartItem = { ...product, cartItemId: Date.now() };
  state.cartItems.push(cartItem);
});

CartStore.register('remove', (state, product) => {
  state.cartItems = state.cartItems.filter(item => item.cartItemId !== product.cartItemId);
});

ObservableStore.onPatch(key, callback)

Registers a callback to be invoked whenever patches are applied to the specified state key.

Kind: static method of ObservableStore

Param Type Description
key string The state key to listen for patches.
callback function The callback to invoke when patches are applied.

Example

appStore.onPatch('posts', (patch) => {
  console.log('Patch applied:', patch);
});

ObservableStore.applyPatch(patches)

Applies the given patches to the store's state.

Kind: static method of ObservableStore

Param Type Description
patches Array The patches to apply to the state.

Example

const patches = [{ op: 'replace', path: ['posts', 0, 'title'], value: 'New Title' }];
appStore.applyPatch(patches);

ObservableStore.query(queryName, config)

Registers a query with the given configuration. This method sets up the query with the provided options and handles refetching based on various triggers like window focus, reconnect, and intervals.

Kind: static method of ObservableStore

Param Type Default Description
queryName string The name of the query to register.
config Object The configuration object for the query.
config.queryKey string | Array The unique key for the query.
config.queryFn function The function to fetch data for the query.
[config.staleTime] number 0 The time in milliseconds before the query is considered stale.
[config.refetchOnWindowFocus] boolean false Whether to refetch the query on window focus.
[config.refetchInterval] number | null The interval in milliseconds to refetch the query.
[config.refetchOnReconnect] boolean true Whether to refetch the query on reconnect.
[config.gcTime] number 300000 The time in milliseconds before garbage collecting the query.
[config.retry] number 1 The number of retry attempts for the query.
[config.retryDelay] function The function to calculate the delay between retries.
[config.onSuccess] function The callback function to execute when the query succeeds. Receives a context object with result, state, actions, mutations, and invalidateQueries.
[config.onError] function The callback function to execute when the query fails. Receives a context object with error, state, actions, mutations, and invalidateQueries.
[config.actions] Object this.actions The actions available in the store.

Example

appStore.register('setPosts', (state, posts) => {
  state.posts = posts;
});

appStore.query('fetchPosts', {
  queryKey: 'posts',
  queryFn: () => fetch('https://api.camijs.com/posts').then(res => res.json()),
  onSuccess: (ctx) => {
    ctx.actions.setPosts(ctx.result);
  },
  onError: (ctx) => {
    // console.error('Query failed:', ctx.error);
  }
});

ObservableStore.fetch(queryName, ...args) ⇒ Promise

Fetches data for the given query name. If the data is cached and not stale, it returns the cached data. Otherwise, it fetches new data using the query function. Supports retry logic and calls lifecycle hooks.

Kind: static method of ObservableStore
Returns: Promise - A promise that resolves to the query result.

Param Type Description
queryName string The name of the query to fetch.
...args any The arguments to pass to the query function.

Example

// Fetching data for a query named 'fetchPosts'
appStore.fetch('fetchPosts')

ObservableStore.invalidateQueries(queryName)

Invalidates the cache and any associated intervals or event listeners for a given query name.

Kind: static method of ObservableStore

Param Type Description
queryName string The name of the query to invalidate.

ObservableStore.mutation(mutationName, config)

Registers a mutation with the given configuration. This method sets up the mutation with the provided options and handles the mutation lifecycle.

Kind: static method of ObservableStore

Param Type Default Description
mutationName string The name of the mutation to register.
config Object The configuration object for the mutation.
config.mutationFn function The function to perform the mutation.
[config.onMutate] function The function to be called before the mutation is performed.
[config.onError] function The function to be called if the mutation encounters an error.
[config.onSuccess] function The function to be called if the mutation is successful.
[config.onSettled] function The function to be called after the mutation has either succeeded or failed.
[config.actions] Object this.actions The actions available in the store.
[config.queries] Object this.queries The queries available in the store.

Example

appStore.mutation('deletePost', {
  mutationFn: (id) => fetch(`https://api.camijs.com/posts/${id}`, { method: 'DELETE' }).then(res => res.json()),
  onMutate: (context) => {
    context.actions.setPosts(context.state.posts.filter(post => post.id !== context.args[0]));
  },
  onError: (context) => {
    context.actions.setPosts(context.previousState.posts);
  },
  onSuccess: (context) => {
    console.log('Mutation successful:', context);
  },
  onSettled: (context) => {
    console.log('Mutation settled');
    context.invalidateQueries('posts');
  }
});

appStore.mutate('deletePost', id);

ObservableStore.mutate(mutationName, ...args) ⇒ Promise

Performs the mutation with the given name and arguments. This method handles the mutation lifecycle, including optimistic updates, success handling, and error handling.

Kind: static method of ObservableStore
Returns: Promise - A promise that resolves to the mutation result.

Param Type Description
mutationName string The name of the mutation to perform.
...args any The arguments to pass to the mutation function.

Example

// Define a mutation named 'deletePost'
appStore.mutation('deletePost', {
  // The function that performs the actual mutation logic
  mutationFn: (id) => fetch(`https://api.camijs.com/posts/${id}`, { method: 'DELETE' }).then(res => res.json()),
  // Optional: Optimistically update the state before the mutation
  onMutate: (context) => {
    context.actions.setPosts(context.state.posts.filter(post => post.id !== context.args[0]));
  },
  // Optional: Handle errors during mutation
  onError: (context) => {
    context.actions.setPosts(context.previousState.posts);
  },
  // Optional: Perform actions after a successful mutation
  onSuccess: (context) => {
    console.log('Mutation successful:', context);
  },
  // Optional: Perform actions after the mutation is settled (success or error)
  onSettled: (context) => {
    console.log('Mutation settled');
    context.invalidateQueries('posts');
  }
});

// Execute the 'deletePost' mutation with a post ID
appStore.mutate('deletePost', 1);

slice(store, options) ⇒ Object

Creates a slice of the store with its own state and actions, namespaced to avoid conflicts.

Kind: global function
Returns: Object - - An object containing the action methods for the slice, including getState, actions, queries, mutations, and subscribe methods.

Param Type Description
store Object The main store instance.
options Object The options for creating the slice.
options.name string The name of the slice.
options.state Object The initial state of the slice.
options.actions Object The actions for the slice.
[options.queries] Object The queries for the slice.
[options.mutations] Object The mutations for the slice.

Example

const appStore = store({
  // Initial state for other parts of the application
});

const postsSlice = slice(appStore, {
  name: 'posts',
  state: [
    { id: 1, title: 'First Post' },
    { id: 2, title: 'Second Post' }
  ],
  actions: {
    updatePost: (state, { id, title }) => {
      const postIndex = state.findIndex(post => post.id === id);
      if (postIndex !== -1) {
        state[postIndex].title = title;
      }
    }
  }
});

// Accessing the slice's state
postsSlice.getState();

// Dispatching actions
postsSlice.actions.updatePost({ id: 1, title: 'Updated Title' });

// Subscribing to state changes
const unsubscribe = postsSlice.subscribe(state => {
  console.log('Posts slice state changed:', state);
});

// Unsubscribe when no longer needed
unsubscribe();

store(initialState, [options]) ⇒ ObservableStore

This function creates a new instance of ObservableStore with the provided initial state and enhances it with localStorage support if enabled. The store's state will be automatically persisted to and loaded from localStorage, using the provided name as the key. The localStorage option enables this behavior and can be toggled off if persistence is not needed.

Kind: global function
Returns: ObservableStore - A new instance of ObservableStore with the provided initial state, enhanced with localStorage if enabled.

Param Type Default Description
initialState Object The initial state of the store.
[options] Object Configuration options for the store.
[options.localStorage] boolean true Whether to use localStorage for state persistence.
[options.name] string "'cami-store'" The name of the store to use as the key in localStorage.
[options.expiry] number 86400000 The time in milliseconds until the stored state expires (default is 24 hours).

Example

// Create a store with default localStorage support
const CartStore = store({ cartItems: [] });

// Create a store without localStorage support
const NonPersistentStore = store({ items: [] }, { localStorage: false });