diff --git a/src/__tests__/useSelector.test.js b/src/__tests__/useSelector.test.js index afe17fc..cce7dd0 100644 --- a/src/__tests__/useSelector.test.js +++ b/src/__tests__/useSelector.test.js @@ -21,7 +21,52 @@ describe('useSelector', () => { store = createStore({}) }) - describe('core subscription behavior', () => { + describe('core select by string subscription behaviour ', () => { + let tester + + beforeEach(() => { + store.setState({ + foo: 'foo', + bar: 'bar' + }) + + const Comp = () => { + const { foo, bar } = useSelector('foo,bar') + + return ( +
+
{foo}
+
{bar}
+
+ ) + } + + tester = ptl.render( + + + + ) + }) + + it('selects the state on intial render', () => { + expect(tester.getByTestId('foo')).toHaveTextContent('foo') + expect(tester.getByTestId('bar')).toHaveTextContent('bar') + }) + + it('selects the state and renders the component when the store updates', () => { + ptl.act(() => { + store.setState({ + foo: 'fooB', + bar: 'barB' + }) + }) + + expect(tester.getByTestId('foo')).toHaveTextContent('fooB') + expect(tester.getByTestId('bar')).toHaveTextContent('barB') + }) + }) + + describe('core selector function subscription behavior', () => { let tester beforeEach(() => { diff --git a/src/index.js b/src/index.js index 9a95499..0afa2ac 100644 --- a/src/index.js +++ b/src/index.js @@ -9,12 +9,26 @@ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffec const refEquality = (a, b) => a === b +// select('foo,bar') creates a function of the form: ({ foo, bar }) => ({ foo, bar }) +const select = (properties) => { + properties = properties.split(/\s*,\s*/) + + return state => { + const selected = {} + for (let i = 0; i < properties.length; i++) { + selected[properties[i]] = state[properties[i]] + } + return selected + } +} + export const StoreContext = createContext(null) export const StoreProvider = StoreContext.Provider export const useStore = () => useContext(StoreContext) +// selector can be a string 'foo,bar' or a function (state => state.foo) export const useSelector = (selector, equalityFn = refEquality) => { const store = useStore() const [, forceRerender] = useReducer(s => s + 1, 0) @@ -22,12 +36,14 @@ export const useSelector = (selector, equalityFn = refEquality) => { const selectorRef = useRef(null) const selectedStateRef = useRef(null) const onChangeErrorRef = useRef(null) + const isSelectorStr = (typeof selector === 'string') let selectedState try { if (selectorRef.current !== selector || onChangeErrorRef.current) { - selectedState = selector(store.getState()) + const state = store.getState() + selectedState = isSelectorStr ? select(selector)(state) : selector(state) } else { selectedState = selectedStateRef.current } @@ -43,7 +59,7 @@ export const useSelector = (selector, equalityFn = refEquality) => { } useIsomorphicLayoutEffect(() => { - selectorRef.current = selector + selectorRef.current = isSelectorStr ? select(selector) : selector selectedStateRef.current = selectedState onChangeErrorRef.current = null })