npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

codethings-nextjs-router-addons

v1.3.1

Published

A few custom hooks exntending the Next.js useRouter functionality.

Downloads

7

Readme

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.