150 lines
5.5 KiB
JavaScript
150 lines
5.5 KiB
JavaScript
/**
|
|
* External dependencies
|
|
*/
|
|
import { QUERY_STATE_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
import { useRef, useEffect, useCallback } from '@wordpress/element';
|
|
import { useQueryStateContext } from '@woocommerce/base-context';
|
|
import { usePrevious } from '@woocommerce/base-hooks';
|
|
import isShallowEqual from '@wordpress/is-shallow-equal';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import { useShallowEqual } from './use-shallow-equal';
|
|
|
|
/**
|
|
* A custom hook that exposes the current query state and a setter for the query
|
|
* state store for the given context.
|
|
*
|
|
* "Query State" is a wp.data store that keeps track of an arbitrary object of
|
|
* query keys and their values.
|
|
*
|
|
* @param {string} [context] What context to retrieve the query state for. If not
|
|
* provided, this hook will attempt to get the context
|
|
* from the query state context provided by the
|
|
* QueryStateContextProvider
|
|
*
|
|
* @return {Array} An array that has two elements. The first element is the
|
|
* query state value for the given context. The second element
|
|
* is a dispatcher function for setting the query state.
|
|
*/
|
|
export const useQueryStateByContext = ( context ) => {
|
|
const queryStateContext = useQueryStateContext();
|
|
context = context || queryStateContext;
|
|
const queryState = useSelect(
|
|
( select ) => {
|
|
const store = select( storeKey );
|
|
return store.getValueForQueryContext( context, undefined );
|
|
},
|
|
[ context ]
|
|
);
|
|
const { setValueForQueryContext } = useDispatch( storeKey );
|
|
const setQueryState = useCallback(
|
|
( value ) => {
|
|
setValueForQueryContext( context, value );
|
|
},
|
|
[ context, setValueForQueryContext ]
|
|
);
|
|
|
|
return [ queryState, setQueryState ];
|
|
};
|
|
|
|
/**
|
|
* A custom hook that exposes the current query state value and a setter for the
|
|
* given context and query key.
|
|
*
|
|
* "Query State" is a wp.data store that keeps track of an arbitrary object of
|
|
* query keys and their values.
|
|
*
|
|
* @param {*} queryKey The specific query key to retrieve the value for.
|
|
* @param {*} [defaultValue] Default value if query does not exist.
|
|
* @param {string} [context] What context to retrieve the query state for. If
|
|
* not provided will attempt to use what is provided
|
|
* by query state context.
|
|
*
|
|
* @return {*} Whatever value is set at the query state index using the
|
|
* provided context and query key.
|
|
*/
|
|
export const useQueryStateByKey = ( queryKey, defaultValue, context ) => {
|
|
const queryStateContext = useQueryStateContext();
|
|
context = context || queryStateContext;
|
|
const queryValue = useSelect(
|
|
( select ) => {
|
|
const store = select( storeKey );
|
|
return store.getValueForQueryKey( context, queryKey, defaultValue );
|
|
},
|
|
[ context, queryKey ]
|
|
);
|
|
|
|
const { setQueryValue } = useDispatch( storeKey );
|
|
const setQueryValueByKey = useCallback(
|
|
( value ) => {
|
|
setQueryValue( context, queryKey, value );
|
|
},
|
|
[ context, queryKey, setQueryValue ]
|
|
);
|
|
|
|
return [ queryValue, setQueryValueByKey ];
|
|
};
|
|
|
|
/**
|
|
* A custom hook that works similarly to useQueryStateByContext. However, this
|
|
* hook allows for synchronizing with a provided queryState object.
|
|
*
|
|
* This hook does the following things with the provided `synchronizedQuery`
|
|
* object:
|
|
*
|
|
* - whenever synchronizedQuery varies between renders, the queryState will be
|
|
* updated to a merged object of the internal queryState and the provided
|
|
* object. Note, any values from the same properties between objects will
|
|
* be set from synchronizedQuery.
|
|
* - if there are no changes between renders, then the existing internal
|
|
* queryState is always returned.
|
|
* - on initial render, the synchronizedQuery value is returned.
|
|
*
|
|
* Typically, this hook would be used in a scenario where there may be external
|
|
* triggers for updating the query state (i.e. initial population of query
|
|
* state by hydration or component attributes, or routing url changes that
|
|
* affect query state).
|
|
*
|
|
* @param {Object} synchronizedQuery A provided query state object to
|
|
* synchronize internal query state with.
|
|
* @param {string} [context] What context to retrieve the query state
|
|
* for. If not provided, will be pulled from
|
|
* the QueryStateContextProvider in the tree.
|
|
*/
|
|
export const useSynchronizedQueryState = ( synchronizedQuery, context ) => {
|
|
const queryStateContext = useQueryStateContext();
|
|
context = context || queryStateContext;
|
|
const [ queryState, setQueryState ] = useQueryStateByContext( context );
|
|
const currentQueryState = useShallowEqual( queryState );
|
|
const currentSynchronizedQuery = useShallowEqual( synchronizedQuery );
|
|
const previousSynchronizedQuery = usePrevious( currentSynchronizedQuery );
|
|
// used to ensure we allow initial synchronization to occur before
|
|
// returning non-synced state.
|
|
const isInitialized = useRef( false );
|
|
// update queryState anytime incoming synchronizedQuery changes
|
|
useEffect( () => {
|
|
if (
|
|
! isShallowEqual(
|
|
previousSynchronizedQuery,
|
|
currentSynchronizedQuery
|
|
)
|
|
) {
|
|
setQueryState(
|
|
Object.assign( {}, currentQueryState, currentSynchronizedQuery )
|
|
);
|
|
isInitialized.current = true;
|
|
}
|
|
}, [
|
|
currentQueryState,
|
|
currentSynchronizedQuery,
|
|
previousSynchronizedQuery,
|
|
setQueryState,
|
|
] );
|
|
return isInitialized.current
|
|
? [ queryState, setQueryState ]
|
|
: [ synchronizedQuery, setQueryState ];
|
|
};
|