Skip to content

ryfylke-react-as/create-api-slice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@ryfylke-react/create-api-slice

Adds automatic loading state to async thunks. Used as a drop-in replacement for @reduxjs/toolkit's createSlice.

If you are setting up a new project, I'd recommend you to rather go for RTK's createAPI. This package was made with the intention for use in existing projects that are otherwise happy to keep using the traditional asyncthunk/slice setup for their API calls.

Usage

yarn add @ryfylke-react/create-api-slice
# or
npm i @ryfylke-react/create-api-slice

Create the slice as you normally would, but make sure state is part of your schema. It should take a type of StateStatus. Use our createAPISlice function instead of createSlice from redux toolkit.

If you want to use another key than state to store the StateStatus, pass in { key: "myCustomKey" } as a second parameter to createAPISlice.

slices/postSlice.ts

import {
  createAPISlice,
  StateStatus,
} from "@ryfylke-react/create-api-slice";

const initialState = {
  ...baseInitialState,
  state: StateStatus.IDLE,
};

const postSlice = createAPISlice({
  name: "post",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getPost.fulfilled, (state, { payload }) => {
        state.post = payload;
      })
      .addCase(getPost.rejected, (state) => {
        state.post = null;
      });
  },
});

export const postReducer = postSlice.reducer;

Then, when you create thunks that require loading state, make sure to append :load to the thunk name.

If you want to change the :load identifier, you can pass in { identifier: ":myCustomIdentifier" } as a second parameter to createAPISlice

slices/postThunks.ts

export const getPost = createAsyncThunk(
  `post/getPost:load`,
  async (slug: string, { rejectWithValue }) => {
    return axios
      .get<PostData[]>(`${API_URL}/posts?slug=${slug}`)
      .then((res) => res.data[0])
      .catch((err) => rejectWithValue(err));
  }
);

Calling dispatch(getPost("...")) will now automatically set the loading state to 1 (PENDING), which will again automatically change to 2 (FULFILLED) or 3 (REJECTED).

Here's how you'd implement this logic on the UI:

// ...
import { StateStatus } from "@ryfylke-react/create-api-slice";

const App = () => {
    const { id } = useParams();
    const dispatch = useDispatch();
    const { state, post } = useSelector((state) => state.post);

    useEffect(() => {
        dispatch(getPost(id));
    }, []);

    if (state === StateStatus.REJECTED) {
        return "Error from server";
    }
    if (state === StateStatus.FULFILLED) {
        return (...);
    }
    return "Loading...";
}

This unfortunately only supports one concurring loading state per slice. This means that if you call two async thunks that both have :load appended - they will both mutate the same loading state.

Options

If you want, you can add a second parameter to createAPISlice, which is an options object of type APISliceOpts:

type APISliceOpts<T> = {
  /** The key that stores the StateStatus in the slice. Default `state`. */
  key?: string;
  /** The identifier used to add loading state. Default `:load` */
  identifier?: string;
  /** Replaces the createSlice function used internally */
  createSliceOverwrite?: (
    options: CreateSliceOptions<T, SliceCaseReducers<T>, string>
  ) => any;
};

About

Adds automatic loading state to async thunks

Resources

License

Stars

Watchers

Forks