Skip to content

Instantly share code, notes, and snippets.

@osbornm
Last active April 1, 2021 22:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save osbornm/b356b9034f974825bc553b3105091092 to your computer and use it in GitHub Desktop.
Save osbornm/b356b9034f974825bc553b3105091092 to your computer and use it in GitHub Desktop.
React: useQueryStringState
import {
useHistory,
useLocation
} from 'react-router-dom';
import queryString from 'query-string';
import { renderHook } from '@testing-library/react-hooks';
import useQueryStringState from './useQueryStringState';
const mockHistory = { push: jest.fn() };
let mockLocation = {
search: 'foo=1',
pathname: 'path'
};
jest.mock('query-string');
jest.mock('react-router-dom', () => ({
useHistory: () => {return mockHistory},
useLocation: () => {return mockLocation}
}));
describe('useQueryStringState', () => {
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
it('Calls query-string help with correct values and options', () => {
renderHook(() => useQueryStringState());
expect(queryString.parse).toHaveBeenCalledWith('foo=1', { parseBooleans: true, parseNumbers: true, arrayFormat: 'comma' });
});
it('returns the parsed query plus the defaults', () => {
(queryString.parse as jest.Mock).mockReturnValue({ foo: 1 });
const { result: { current } } = renderHook(() => useQueryStringState({ baz: 'buz', foo: 2 }));
expect(current[0]).toEqual({
foo: 1,
baz: 'buz'
});
});
it('returns a set function that stringifies and calls push', () => {
(queryString.stringify as jest.Mock).mockReturnValue('stringifiedValue');
const { result: { current } } = renderHook(() => useQueryStringState({ baz: 'buz', foo: 2 }));
const setState = current[1];
setState({ value: 'foo', array: [1, 2, 3] });
expect(queryString.stringify).toHaveBeenCalledWith({ value: 'foo', array: [1, 2, 3] }, { arrayFormat: 'comma' });
expect(mockHistory.push).toHaveBeenCalledWith({ pathname: 'path', search: 'stringifiedValue' });
});
it('removes defaults when writing queryString', () => {
const { result: { current } } = renderHook(() => useQueryStringState({defaultToRemove: true}));
const setState = current[1];
setState({ value: 'foo', defaultToRemove: true});
expect(queryString.stringify).toHaveBeenCalledWith({ value: 'foo'}, { arrayFormat: 'comma' });
})
});
import {
useHistory,
useLocation
} from "react-router-dom";
import queryString from 'query-string';
import { useMemo } from 'react';
export default function useQueryStringState(defaults = {}): [any | undefined, (any) => void] {
const history = useHistory();
const location = useLocation();
const queryStringState = useMemo(() => {
const parsedQuery = queryString.parse(location.search, { parseBooleans: true, parseNumbers: true, arrayFormat: 'comma' });
return {
...defaults,
...parsedQuery
};
}, [location.search, defaults]);
const updateQueryStringState = (newState) => {
Object.keys(newState).forEach(k => {
if(newState[k] === defaults[k]) {
delete newState[k];
}
})
const qs = queryString.stringify(newState, { arrayFormat: 'comma' });
history.push({
pathname: location.pathname,
search: qs
});
};
return [queryStringState, updateQueryStringState];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment