In this guide, we will walk through the steps to set up a global state management library.
npm i
npm run build:css
export const createStore = (reducer: Reducer, initialState: StoreState) => {
const store: Store = {
state: initialState,
listeners: [],
dispatch: () => {},
subscribe: () => {},
getState: () => store.state,
};
store.subscribe = (listener) => {
store.listeners.push(listener);
};
store.dispatch = (action) => {
console.log("> Action", action);
store.state = reducer(store.state, action);
store.listeners.forEach((listener) => listener());
};
return store;
};
export type Store = {
state: StoreState;
listeners: (() => void)[];
getState: () => StoreState;
subscribe: (listener: () => void) => void;
dispatch: (action: Action) => void;
};
export const getInitialState = () => ({
// This would be a redux thunk action that calls our API
bookList: books,
booksInBasket: [],
});
export type Book = {
title: string;
author: string;
quantity: number;
};
export type StoreState = {
bookList: Book[];
booksInBasket: Book[];
};
export const addBookToBasketAction = (
title: string,
author: string,
quantity: number
): Action => ({
type: "book/addBookToBasket",
payload: { title, author, quantity },
});
export type Action = {
type: string;
payload: Book;
};
export const reducer: Reducer = (prevState, action) => {
switch (action.type) {
case "book/addBookToBasket": {
return prevState;
}
default: {
return prevState;
}
}
};
export type Reducer = (state: StoreState, action: Action) => StoreState;
export const store = createStore(reducer, getInitialState());
export const renderSupplierStoreList = (bookList: Book[]) =>
renderHTMLElement(bookList, "supplierStore", "add", "book/addBookToBasket");
renderSupplierStoreList(store.getState().bookList);
store.subscribe(() => {
const state = store.getState();
console.log("> State changed:", state);
renderSupplierStoreList(state.bookList);
});
— Build and run application —
npm run build
npm run start
const findBook = (arr: Book[]) =>
arr.find(
(book) =>
book.author === action.payload.author &&
book.title === action.payload.title
);
switch (action.type) {
case "book/addBookToBasket": {
if (action.payload.author && action.payload.title) {
// updates supplier list
const bookInList = findBook(prevState.bookList);
if (bookInList && bookInList.quantity > 0) {
const updatedBookList = prevState.bookList.map((book) => {
if (
book.author === action.payload.author &&
book.title === action.payload.title
) {
return {
...book,
quantity: book.quantity - 1,
};
}
return book;
});
// updates customer basket
const bookInBasket = findBook(prevState.booksInBasket);
let updatedBooksInBasket = [...prevState.booksInBasket];
if (!bookInBasket) {
updatedBooksInBasket.push({ ...bookInList, quantity: 1 });
} else {
updatedBooksInBasket = updatedBooksInBasket.map((book) => {
if (
book.author === action.payload.author &&
book.title === action.payload.title
) {
return {
...book,
quantity: book.quantity + 1,
};
}
return book;
});
}
return {
...prevState,
bookList: updatedBookList,
booksInBasket: updatedBooksInBasket,
};
}
console.error("Book list has quantity of 0");
return prevState;
}
console.error("Need title and author to manipulate state");
return prevState;
}
}
— build —
npm run build
export const renderCustomerBasket = (booksInBasket: Book[]) =>
renderHTMLElement(
booksInBasket,
"customerBasket",
"remove",
"book/removeBookFromBasket"
);
export const renderCustomerDashboard = (booksInBasket: Book[]) =>
renderHTMLElement(
booksInBasket,
"customerDashboard",
"remove",
"book/removeBookFromBasket"
);
renderCustomerBasket(state.booksInBasket);
renderCustomerDashboard(state.booksInBasket);
— build —
case "book/removeBookFromBasket": {
if (action.payload.author && action.payload.title) {
// updates customer basket
const bookInBasket = findBook(prevState.booksInBasket);
if (!bookInBasket) {
console.error("Book not found in basket");
return prevState;
}
let updatedBooksInBasket = [...prevState.booksInBasket];
if (bookInBasket.quantity === 1) {
updatedBooksInBasket = updatedBooksInBasket.filter(
(book) =>
book.author !== action.payload.author ||
book.title !== action.payload.title
);
} else {
updatedBooksInBasket = updatedBooksInBasket.map((book) => {
if (
book.author === action.payload.author &&
book.title === action.payload.title
) {
return {
...book,
quantity: book.quantity - 1,
};
}
return book;
});
}
// updates supplier list
const updatedBookList = prevState.bookList.map((book) => {
if (
book.author === action.payload.author &&
book.title === action.payload.title
) {
return {
...book,
quantity: book.quantity + 1,
};
}
return book;
});
return {
...prevState,
bookList: updatedBookList,
booksInBasket: updatedBooksInBasket,
};
}
console.error("Need author and title to identify book");
return prevState;
}
— build —