Skip to content

Instantly share code, notes, and snippets.

@mattiaerre
Created April 29, 2020 02:12
Show Gist options
  • Save mattiaerre/8dbd2d8efca3f242c7085a9ce82ecbde to your computer and use it in GitHub Desktop.
Save mattiaerre/8dbd2d8efca3f242c7085a9ce82ecbde to your computer and use it in GitHub Desktop.
React hook useReducerWithLocalStorage
import { useState } from 'react';
// credit: https://usehooks.com/useLocalStorage/
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
function setValue(value) {
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
}
}
return [storedValue, setValue];
}
export default useLocalStorage;
import { useReducer } from 'react';
import useLocalStorage from './useLocalStorage';
function useReducerWithLocalStorage({ initializerArg, key, reducer }) {
const [localStorageState, setLocalStorageState] = useLocalStorage(
key,
initializerArg
);
return useReducer(
(state, action) => {
const newState = reducer(state, action);
setLocalStorageState(newState);
return newState;
},
{ ...localStorageState }
);
}
export default useReducerWithLocalStorage;
import { renderHook, act } from '@testing-library/react-hooks';
import useLocalStorage from './useLocalStorage';
import useReducerWithLocalStorage from './useReducerWithLocalStorage';
jest.mock('./useLocalStorage');
const emptyState = { firstName: '', lastName: '' };
const mockSetLocalStorageState = jest.fn();
const mockLocalStorageState = { ...emptyState, lastName: 'Doe' };
useLocalStorage.mockImplementation(() => [
mockLocalStorageState,
mockSetLocalStorageState
]);
const REACT_APP_STATE = 'REACT_APP_STATE';
const CHANGE_FIRST_NAME = 'CHANGE_FIRST_NAME';
function reducer(state, action) {
switch (action.type) {
case CHANGE_FIRST_NAME:
return { ...state, firstName: action.value };
default:
return state;
}
}
const mockReducer = jest.fn((state, action) => reducer(state, action));
test('useReducerWithLocalStorage', () => {
const {
result: {
current: [state, dispatch]
}
} = renderHook(() =>
useReducerWithLocalStorage({
initializerArg: emptyState,
key: REACT_APP_STATE,
reducer: mockReducer
})
);
expect(useLocalStorage).toBeCalledWith(REACT_APP_STATE, emptyState);
expect(state).toEqual(mockLocalStorageState);
act(() => {
dispatch({ type: CHANGE_FIRST_NAME, value: 'John' });
});
expect(mockSetLocalStorageState).toBeCalledWith({
firstName: 'John',
lastName: 'Doe'
});
});
@mattiaerre
Copy link
Author

how about this?

import { useReducer } from 'react';
import remove from './remove';
import useLocalStorage from './useLocalStorage';

function useReducerWithLocalStorage({
  blacklist = [],
  initializerArg,
  key,
  reducer
}) {
  const [localStorageState, setLocalStorageState] = useLocalStorage(
    key,
    remove({ blacklist, state: initializerArg })
  );

  return useReducer(
    (state, action) => {
      const newState = reducer(state, action);
      setLocalStorageState(remove({ blacklist, state: newState }));
      return newState;
    },
    { ...initializerArg, ...localStorageState }
  );
}

export default useReducerWithLocalStorage;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment