1.2.9 • Published 2 years ago

create-async-slice v1.2.9

Weekly downloads
-
License
ISC
Repository
github
Last release
2 years ago

Create async slice

Helper for creating async slices with redux toolkit to avoid boilerplates

API

createAsyncSlice

Returns redux-toolit slice object with 4 actions:
1. request - to dispatch request with payload for fetching data and set isProcessing: true 2. success - to fulfill your store with success data from API 3. error - to fulfill data with error from API response 4. reset - to reset store's state

createAsyncMappingSlice

Stands for creating relations between async states or for making complicated async lists slices. Example: you're users are toggleable sections and when you toggle each section - you need to load company info for that certain user. So there's multiple loading companies for each opened user at the same time

Returns redux-toolit slice object with 4 actions:
1. request - to dispatch request with payload for fetching data and set isProcessing: true Expects { id } as default. id is id of item in list which needs to get additional data e.g. company user id. 1. success - to fulfill your store with success data from API. Expects { id, value } as default. value is your data you want put into your redux store 2. error - to fulfill data with error from API response. Expects { id, error } as default. 3. reset - to reset store's state. Expects { id } as default.

How does async slice's state look like?

// state.users
// createAsyncSlice:
{
  getUsers: {
    value: [{ name: 'Test user', id: 'user-id-1' }],
    isSuccess: true,
    isProcessing: false,
    error: null
  }
}

// createAsyncMappingSlice:
{
  getUserCompanies: {
    ['user-id-1']: {
      value: [{ name: 'Test Company', id: 'company-id-1' }],
      isSuccess: true,
      isProcessing: false,
      error: null
    }
  }
}

Usage

src/ducks/users/users.slices.ts

import { combineReducers } from 'redux';
import { createAsyncSlice } from 'create-async-slice';

export const getUsersSlice = createAsyncSlice<RequestPayload, SuccessPayload, ErrorPayload>({
  name: 'getUsers',
  selectAsyncState: (state) => state.users.getUsers // To get `getUsersSlice.selectors` work
});

/* RequestPayloadWithId, SuccessPayloadWithId, ErrorPayloadWithId:
these are optional since we always should pass an id of parent list by default
*/
export const getUserCompaniesSlice = createAsyncMappingSlice<RequestPayloadWithId, SuccessPayloadWithId, ErrorPayloadWithId>({
  name: 'getCompaiesByUserId',
  selectAsyncState: (state) => state.users.getCompaiesByUserId // To get `getUserCompaniesSlice.selectors` work
})

export const usersReducer = combineReducers({
  getUsers: getUsersSlice.reducer,
  getUserCompanies: getUserCompaniesSlice.reducer
});

src/ducks/users/users.sagas.ts

function* getUserCompaniesSaga({ payload }) {
  // id - companyId
  const { id } = payload;

  try {
    const { data } = yield call(usersApi.getUserCompanies, id);

    yield put(getUserCompaniesSlice.actions.success(data));
  } catch (e) {
    yield put(getUserCompaniesSlice.actions.error(e.message));
  }
}

function* getUsersSaga() {
  try {
    const { data } = yield call(usersApi.getUsers);

    yield put(getUsersSlice.actions.success(data));
  } catch (e) {
    yield put(getUsersSlice.actions.error(e.message));
  }
}

export function* usersSagas() {
  yield takeAll(getUsersSlice.actions.request, getUsersSaga);

  yield takeAll(getUserCompaniesSlice.actions.request, getUserCompaniesSaga);
}

src/store/reducers.ts

import { usersReducer } from '@ducks/users/users.slices';

export const reducers = {
  users: usersReducer,
};

src/components/Users.ts

import React, { useSelector, useDispatch, useEffect } from 'react';

import { getUsersSlice } from '@ducks/users/users.slice';
import { selectIsGetUsersProcessing, selectGetUsersData } from '@ducks/users/users.selectors';

export const Users = () => {
  const dispatch = useDispatch();

  const isProcessing = useSelector(getUsersSlice.selectors?.isProcessing);

  const usersData = useSelector(getUsersSlice.selectors?.value)

  useEffect(() => {
    dispatch(getUsersSlice.actions.request());
  }, []);

  return (
    <div>
      {usersData.map(user => (
        <User {...user}>
      ))}
    </div>
  )
}

src/components/User.ts

import React, { useSelector, useDispatch, useEffect } from 'react';

import { getUserCompaniesSlice } from '@ducks/users/users.slice';
import { selectIsGetUserCompanyProcessing, selectGetUserCompaniesByIdData } from '@ducks/users/users.selectors';

export const User = ({ id, name }) => {
  const dispatch = useDispatch();

  const isProcessing = useSelector(state => getUserCompaniesSlice.selectors?.isProcessing(state, { id }));

  const companiesData = useSelector(state => getUserCompaniesSlice.selectors?.value(state, { id }));

  useEffect(() => {
    dispatch(getUserCompaniesSlice.actions.request({ id }));
  }, [id]);

  return (
    <div>
      {companiesData.map(company => (
        <Company {...company}>
      ))}
    </div>
  )
}

API

Async Slice Options:

createAsyncSlice accepts all createSlice options and following:

Option keyDescriptionDefault Value
selectAsyncState(state) => AsyncState, optional. Redux selector which returns your async slice state (needed for using createAsyncSlice({ ... }).selectors). E.g: selectAsyncState: (state) => state.users.getUsers

createAsyncMappingsSlice accepts all createAsyncSlice options and following: | Option key | Description | Default Value | | ------------- | ------------- | ------------- | | mappingsAmountLimit | number, optional. Limits max amount of items stored in slices state (to avoid memory leak) | undefined |

1.2.9

2 years ago

1.2.8

2 years ago

1.2.7

2 years ago

1.2.6

2 years ago

1.2.5

2 years ago

1.2.4

2 years ago

1.2.3

2 years ago

1.2.1

2 years ago

1.2.0

2 years ago

1.0.14

2 years ago

1.0.13

2 years ago

1.0.12

2 years ago

1.0.10

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago