1.3.1 • Published 5 years ago

codethings-nextjs-router-addons v1.3.1

Weekly downloads
3
License
ISC
Repository
github
Last release
5 years ago

codethings-nextjs-router-addons

Created a few custom hooks extending the Next.js useRouter functionality.

Install

npm install codethings-nextjs-router-addons    
    
yarn add codethings-nextjs-router-addons    

useUrlParam

const { 
  paramValue,
  paramCollection
  updateParam, 
  clearParam, 
} = useUrlParam(paramName);

Read param(s)

paramValue is the raw string version of the param values
paramCollection is an array version of the param values

https://url.com/?paramName=someParamValue

results in:

paramValue === 'someParamValue'
paramCollection === ["someParamValue"]
https://url.com/?paramName=param1,param2,param3

results in:

paramValue === 'param1,param2,param3'
paramCollection === ['param1', 'param2', 'param3']

Set param(s)

updateParam and clearParam will update the active param while persisting any other values.

updateParam accepts either a string or array of values.

Param can be removed by either calling clearParam() or updateParam(null) (anything falsy)

Updates are pushed to the browser history stack and accessible through the browser navigation.

Override variable names

Optionally overide the variable names for clarity or to use the hook multiple times in the same component.

const { 
  paramValue: timestampValue, 
  updateParam: updateTimestamp, 
  clearParam: clearTimestamp, 
} = useUrlParam('timestamp');

Custom Hook Wrapper

If using the hook for the same param in many places, create and export a wrapper hook to encapsulate the param and custom variable naming.

import { useUrlParam } from 'codethings-nextjs-router-addons';

export const PARAM_NAME_KEYWORD_SEARCH = 'search';

export function useKeywordSearchFilter () {
 
  const { 
    paramValue: keywordSearchValue, 
    updateParam: updateKeywordSearch, 
    clearParam: clearKeywordSearch,  
  } = useUrlParam(PARAM_NAME_KEYWORD_SEARCH);

  return { 
    keywordSearchValue,
    updateKeywordSearch,
    clearKeywordSearch
  };
}

Example: Fetching search results from URL param

Capture updates to history stack and use that to make api queries when the url state changes.

Search Input Component

Using the useKeywordSearchFilter wrapper hook just made above:

import { useKeywordSearchFilter } from '.utils/useKeywordSearchFilter';

const { 
  updateKeywordSearch, 
  clearKeywordSearch
} = useKeywordSearchFilter();

const [searchInput, setSearchInput] = useState();

<form>
  <input value={searchInput} onChange={(e) => setSearchInput(e.target.value)}>
  <button onClick={() => updateKeywordSearch(searchInput)}>
  <span className="clear" onClick={() => { 
    setSearchInput('');
    clearKeywordSearch();
  }>Clear</span>
</form>

Search Results Component

then use the param value to trigger a fetch (or gql query or reducer dispatch, etc.) when URL params update.

import { 
  useKeywordSearchFilter, 
  PARAM_NAME_KEYWORD_SEARCH 
} from '../utils/useKeywordSearchFilter';

const { 
  keywordSearchValue, 
} = useKeywordSearchFilter();

const [searchResults, setSearchResults] = useState(null);

useEffect(() => {
  const abortController = new AbortController();

  const fetchData = async () => {
    try {
      const params = !!keywordSearchValue ? ?${PARAM_NAME_KEYWORD_SEARCH}={keywordSearchValue}` : '';
      const path = `api/resource/${params}`;
      const response = await fetch(path, { signal: abortController.signal });
      const { searchObject } = await response.json();
      setSearchResults(searchObject);
    } catch (e) { 
      if (!abortController.signal.aborted) {
        console.log('search api error: ', e.message);
      }
    }
  };

  fetchData();

  return () => abortController.abort();
}, [keywordSearchValue]);

Filtered results are rendered on the page either from calling updateKeywordSearch anywhere in the code or directly navigating to the url with the param value.

Abort Controller - allows cleaning up pending requests if component unmounts mind async
Thanks Yurui - https://dev.to/pallymore/clean-up-async-requests-in-useeffect-hooks-90h

UrlParamCategoryFilter

The Url Param Category Filter extends useUrlParam by constraining the possible valid values provided in the url against a fixed set of categories.

UrlParamCategoryFilterProvider

const foodCategories = [
  { id: 'fruit', name: 'Fruit' },
  { id: 'vegetable', name: 'Vegetable' },
  { id: 'bakedGoods', name: 'Baked Goods' }
];

<UrlParamCategoryFilterProvider filterCategories={foodCategories}>
  <SomeChildComponentAboutFood>
</UrlParamCategoryFilterProvider> 

useUrlParamCategoryFilter

Access the filter props and methods by using the useUrlParamCategoryFilter in any component within the UrlParamCategoryFilterProvider

const { 
  filterCategories,
  activeFilterId,       
  activeFilterName, 
  updateFilter, 
  clearFilter 
} = useUrlParamCategoryFilter(paramName);

where activeFilterId matches to the param in the URL
and activeFilterName is the corresponding name from the filterCategories collection.

If param value is missing or an array of values, these will be set to null

Example: In-page filter

In some component we make a dropdown selector for the filter:

const { 
  filterCategories: foodCategories,
  activeFilterId: activeFoodCategoryId,
  activeFilterName: activeFoodCategoryName, 
  updateFilter: updateFoodCategory, 
  clearFilter: clearFoodCategory 
} = useUrlParamCategoryFilter('foodCategory');

<div className="dropdown">
  <div className="dropdown-trigger">{activeFoodCategoryName || 'Make A Selection'}</div>
  <div className="dropdown-window>
    {foodCategories.map((foodItem, index) => {
      const { id, catgory } = foodItem;
      const hasActiveFoodCategoryId = category.id === activeFoodCategoryId;

      return (
        <div className="food-category" 
          key={id}
          data-is-active={hasActiveFoodType}
            onClick={() => { 
              if ( !hasActiveFoodType ) { updateFoodCategory(category.id); }
              else { clearFoodCategory(); }
            }>
          {category.name}
        </div>
      );
    }}  
  </div>
</div>

and then in another component somewhere render a list based on if each item has that category property

const { 
  activeFilterId: activeFoodType 
} = useUrlParamCategoryFilter('foodCategory');

{foodItems.map((foodItem, index) => {
  const { id, category } = foodItem;
  const hasActiveFoodType = category.id === activeFoodType;
  const showItem = !activeFoodType || hasActiveFoodType;

  return showItem 
    ? <SomeFoodItemComponent foodItem={foodItem} key={id} />
    : <React.Fragment key={id} />;
})}

Multiple Category filters on the same page

Currently only supports one category provider per page.

1.3.1

5 years ago

1.3.0

5 years ago

1.2.8

5 years ago

1.2.7

5 years ago

1.2.6

5 years ago

1.2.5

5 years ago

1.2.4

5 years ago

1.2.3

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.0

5 years ago

1.0.11

5 years ago

1.0.12

5 years ago

1.0.10

5 years ago

1.0.9

5 years ago

1.0.8

5 years ago

1.0.7

5 years ago

1.0.6

5 years ago

1.0.5

5 years ago