@aredant/use-query-manager
TypeScript icon, indicating that this package has built-in type declarations

2.0.1 • Public • Published

React Use Query

A fast and efficient library for making requests and manage state result in a global level, that give data access from all components of any level, using in React with cache and browser event superpowers. From version 2 was itroduced a custom global state management system, check it out here.

npm version GitHub license Maintenance

New version of react use query library. Check on npm and github.

🎉 Version 2.0.x is live 🎉

Check out for changes in the CHANGELOG:

Changelog

Supporting the project

Maintaining a project takes time. To help allocate time, you can Buy Me a Coffee 😉

Buy Me A Coffee

What is React Use Query?

Package to manage all types of queries, using useQuery(url<String>, options<QueryOptions>) hook, with cache control system and granular access to context state. It can be used to optimize all request process.

It is fast and don't make useless request thanks to cache control system. It can also give you access to context state everywhere in your app thanks to useQueryState(name) hook and <QueryProvider>.

If you need to trigger request just after an event like button click, in an efficient way, you should use useQueryEvent(url<String>, options<QueryOptions>) hook instead (example here).

Contents

  1. Install
  2. Get Started
  3. Options
  4. Returns
  5. Usage Examples
  6. Global state management

Install

Inside your project run on terminal:

npm i @aredant/use-query-manager

or

yarn add @aredant/use-query-manager

The only one dependency that will be installed is @aredant/use-query-manager.

Get Started

Import the package on your main file and wrap project inside QueryProvider.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from "@aredant/use-query-manager"

import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider>
    <App />
  </QueryProvider>,
)

Options

Following the list of all avaiable options of useQuery hook:

Name Type Description
name String It should contains name string for context state granular control.
selector String It should contains key value to select from result object.
pick Function or Array<String> It rappresent the function or the array to pick just a portion of data.
transform Function It rappresent the funcion to transform data before saving on state.
method String It should be one of "GET", "POST", "PUT", "PATCH", "DELETE".
headers Object Headers must be an object. It should contains request headers key value.
body Any Request body data
isDebuggerActivated Boolean It should be activated if you need to debug all process.
cacheTimeout Number It rappresent the timeout to remove cached data from memory in milliseconds.

Following the list of all avaiable options of useQueryEvent hook:

Name Type Description
name String It should contains name string for context state granular control.
selector String It should contains key value to select from result object.
pick Function or Array<String> It rappresent the function or the array to pick just a portion of data.
transform Function It rappresent the funcion to transform data before saving on state.
method String It should be one of "GET", "POST", "PUT", "PATCH", "DELETE".
headers Object Headers must be an object. It should contains request headers key value.
body Any Request body data
isDebuggerActivated Boolean It should be activated if you need to debug all process.
cacheTimeout Number It rappresent the timeout to remove cached data from memory in milliseconds.

Returns

useQuery hook return an object with following keys:

Name Type Description
data Any Data returned from request
error Error or null Request error.
loading Boolean Request loading state.
mutate Function(data<ReactState>) Mutate function to manipulate data state, available everywhere inside QueryProvider.
refresh Function Data refresh function.
cache Object Cache control function: get(url<String>), has(url<String>), clear()

useQueryEvent hook return an object with following keys:

Name Type Description
sendRequest Function Trigger function to send request
isSending Boolean State of sending status
data Any Data returned from request
error Error or null Request error.
loading Boolean Request loading state.
mutate Function(data<ReactState>) Mutate function to manipulate data state, available everywhere inside QueryProvider.
refresh Function Data refresh function.
cache Object Cache control function: get(url<String>), has(url<String>), clear()

Usage Examples

Following usage example:

Basic

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from "@aredant/use-query-manager"

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider>
    <App />
  </QueryProvider>,
)

App.jsx

import { useEffect } from 'react'
import { useQuery } from '@aredant/use-query-manager'

import InnerComponent from './InnerComponent'

const App = () => {
  const { data, error, loading, mutate, refresh, cache } = useQuery("https://dummyjson.com/products", {
    name: "products", // State name to select right context
    selector: "products", // Selector for first level request data
    method: "GET", // Request method
    headers: {}, // Request headers
    body: undefined, // Request body
    transform: (data) => { // Transform response data
      return data.filter((item) => item.id % 2 === 0);
    },
    pick: (key, value) => { // Pick a portion of data
      if (typeof value === "string" || key === "images") return undefined;
      return value;
    },
    cacheTimeout: 5000, // Timeout to auto-clear cache, 0 if you don't want to auto-clear cache
    isDebuggerActivated: true, // -> Take a look to the inspector console 
  });

  useEffect(() => {
    console.log(data, error, loading);
  }, [data, error, loading]);

  return (
    <>
      <pre>
        <InnerComponent />
      </pre>
    </>
  )
}

export default App

InnerComponent.js

import {useQueryState} from '@aredant/use-query-manager'

const InnerComponent = () => {
  const [data, setData] = useQueryState(); // Get all available query data
  const [products, setProducts] = useQueryState("products"); // Get just a portion of data by name

  return (
    <pre>
        {data && JSON.stringify(data, null, 2)}
        {products && JSON.stringify(products, null, 2)}
    </pre>
  )
}

export default InnerComponent;

Pagination

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from "@aredant/use-query-manager"

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider>
    <App />
  </QueryProvider>,
)

App.jsx

import { useState, useEffect } from 'react'
import { useQuery } from '@aredant/use-query-manager'

const API_URL = "https://dummyjson.com/products";

const _formatSkip = (limit, page) => {
  return limit * (page - 1);
}

const App = () => {
  const [page, setLimit] = useState(1);
  const [limit, setPage] = useState(10);
  const [url, setUrl] = useState(`${API_URL}?limit=${limit}&skip=${_formatSkip(limit, page)}`);

  const { data: products, error, loading, mutate, refresh, cache } = useQuery(url, {
    name: "products", // State name to select right context
    selector: "products", // Selector for first level request data
    method: "GET", // Request method
    headers: {}, // Request headers
    body: undefined, // Request body
    transform: (data) => { // Transform response data
      return data.filter((item) => item.id % 2 === 0);
    },
    // pick: (key, value) => { // Pick a portion of data
    //   if (typeof value === "string" || key === "images") return undefined;
    //   return value;
    // },
    pick: ["id", "title", "description"], // Pick a portion of data using array of key name
    cacheTimeout: 5000, // Timeout to auto-clear cache, 0 if you don't want to auto-clear cache
    isDebuggerActivated: true, // -> Take a look to the inspector console 
  });

  const handlePrevPage = () => {
    if (page > 1) setPage((page) => page - 1);
  }
  
  const handleNextPage = () => {
    if (page < Math.ceil(products.length / limit)) setPage((page) => page + 1);
  }

  useEffect(() => {
    setUrl(`${API_URL}?limit=${limit}&skip=${_formatSkip(limit, page)}`);
  }, [page, limit]);

  return (
    <>
      <div>
        <button onClick={handlePrevPage}>Prev</button>
        <span>{page}</span>
        <button onClick={handleNextPage}>Next</button>
      </div>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          {
            products && products.map(({ id, title, description }) => (
              <tr key={id}>
                <td>{id}</td>
                <td>{title}</td>
                <td>{description}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    </>
  )
}

export default App

Request on event

If you want to trigger the request on event like click you can use useQueryEvent hook. Check the following example:

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from "@aredant/use-query-manager"

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider>
    <App />
  </QueryProvider>,
)

App.jsx

import { useEffect, useState } from 'react';
import { useQueryState, useQueryEvent } from './hooks';

import "./App.css";

const App = () => {
  const [id, setId] = useState(1);
  const [url, setUrl] = useState(`https://dummyjson.com/products/${id}`);

  const { sendRequest, isSending, data, error, loading, refresh } = useQueryEvent(url, {
    name: "product",
    isDebuggerActivated: true
  });

  useEffect(() => {
    setUrl(`https://dummyjson.com/products/${id}`);
  }, [id])

  return (
    <>
      <div>
        <input type="text" value={id} onInput={({ target }) => setId(target.value)} />
        <button onClick={sendRequest} disabled={isSending}>Send Request</button>
      </div>
      <div>
        {
          loading ? (
            <p>Loading...</p>
          )
          :
          error ? (
            <p>{error.message}</p>
          )
          :
          (
            <pre>
              {
                data && JSON.stringify(data, null, 2)
              }
            </pre>
          )
        }
      </div>
    </>
  )
}

export default App

Of course you can use a combination of useQuery and useQueryEvent inside the same component.

Using selector

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from "@aredant/use-query-manager"

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider>
    <App />
  </QueryProvider>,
)

App.jsx

import { useEffect } from 'react'
import { useQuery } from '@aredant/use-query-manager'

import InnerComponent from './InnerComponent'

const App = () => {
  const { data, error, loading, mutate, refresh, cache } = useQuery("https://dummyjson.com/products", {
    name: "products", // State name to select right context
    selector: "products", // Selector for first level request data
    method: "GET", // Request method
    headers: {}, // Request headers
    body: undefined, // Request body
    transform: (data) => { // Transform response data
      return data.filter((item) => item.id % 2 === 0);
    },
    pick: (key, value) => { // Pick a portion of data
      if (typeof value === "string" || key === "images") return undefined;
      return value;
    },
    cacheTimeout: 5000, // Timeout to auto-clear cache, 0 if you don't want to auto-clear cache
    isDebuggerActivated: true, // -> Take a look to the inspector console 
  });

  useEffect(() => {
    console.log(data, error, loading);
  }, [data, error, loading]);

  return (
    <>
      <pre>
        <InnerComponent />
      </pre>
    </>
  )
}

export default App

InnerComponent.js

import { useQuerySelector } from '@aredant/use-query-manager'

const InnerComponent = () => {
  const products = useQuerySelector((state) => state.products); // Get just a portion of data by name using query selector callback function

  return (
    <pre>
        {products && JSON.stringify(products, null, 2)}
    </pre>
  )
}

export default InnerComponent;

Global state management

From version 2.0.0 is available a fully featured global state management system.

Work with global State

First of all you need to setup a store using createQueryStore and all dispatchers usign createDispatcher and pass them to QueryProvider as follow:

store.js

import { createQueryStore } from '@aredant/use-query-manager'

export default createQueryStore({
  auth: { // !IMPORTANT Must match with dispatcher name
    token: null,
    user: null,
  }
});

dispatchers/authDispatcher.js

import { createQueryDispatcher } from '@aredant/use-query-manager'

export default createQueryDispatcher({
  name: "auth", // !IMPORTANT Must match with store key object name
  actions: {
    login: (state, payload) => {
      state.token = payload.token;
      state.user = payload.user;
    },
    logout: (state) => {
      state.token = null;
      state.user = null;
    }
  }
});

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

import { QueryProvider } from '@aredant/use-query-manager'

import store from './store'
import authDispatcher from './dispatchers/authDispatcher'

import './index.css'

const dispatchers = [
  authDispatcher,
]

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryProvider store={store} dispatchers={dispatchers}>
    <App />
  </QueryProvider>,
)

Then you can use dispatcher inside any component of your app.

App.jsx

import { useQueryState, useQuerySelector, useQueryDispatcher } from '@aredant/use-query-manager';

import "./App.css";

const App = () => {
  const [data, setData] = useQueryState("auth");

  const auth = useQuerySelector(({ auth }) => auth);
  const { login, logout } = useQueryDispatcher("auth");

  return (
    <>
      <div>
        <p>Token: {auth.token}</p>
        <p>User: {auth.user}</p>
        <button onClick={() => login({ token: "1234abcd", user: "Alex" })}>Login</button>
        <button onClick={() => logout()}>Logout</button>
      </div>
      <div className="container">
        <pre>
            // ====================== <br />
            // -------- DATA -------- <br />
            // ======================
            <br />
            <br />
            {data && JSON.stringify(data, null, 2)}
        </pre>
      </div>
    </>
  )
}

export default App

Dependencies (0)

    Dev Dependencies (1)

    Package Sidebar

    Install

    npm i @aredant/use-query-manager

    Weekly Downloads

    16

    Version

    2.0.1

    License

    MIT

    Unpacked Size

    472 kB

    Total Files

    29

    Last publish

    Collaborators

    • alexdant91org