2.0.1 • Published 1 year ago

@aredant/use-query-manager v2.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

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 😉

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:

NameTypeDescription
nameStringIt should contains name string for context state granular control.
selectorStringIt should contains key value to select from result object.
pickFunction or Array<String>It rappresent the function or the array to pick just a portion of data.
transformFunctionIt rappresent the funcion to transform data before saving on state.
methodStringIt should be one of "GET", "POST", "PUT", "PATCH", "DELETE".
headersObjectHeaders must be an object. It should contains request headers key value.
bodyAnyRequest body data
isDebuggerActivatedBooleanIt should be activated if you need to debug all process.
cacheTimeoutNumberIt rappresent the timeout to remove cached data from memory in milliseconds.

Following the list of all avaiable options of useQueryEvent hook:

NameTypeDescription
nameStringIt should contains name string for context state granular control.
selectorStringIt should contains key value to select from result object.
pickFunction or Array<String>It rappresent the function or the array to pick just a portion of data.
transformFunctionIt rappresent the funcion to transform data before saving on state.
methodStringIt should be one of "GET", "POST", "PUT", "PATCH", "DELETE".
headersObjectHeaders must be an object. It should contains request headers key value.
bodyAnyRequest body data
isDebuggerActivatedBooleanIt should be activated if you need to debug all process.
cacheTimeoutNumberIt rappresent the timeout to remove cached data from memory in milliseconds.

Returns

useQuery hook return an object with following keys:

NameTypeDescription
dataAnyData returned from request
errorError or nullRequest error.
loadingBooleanRequest loading state.
mutateFunction(data<ReactState>)Mutate function to manipulate data state, available everywhere inside QueryProvider.
refreshFunctionData refresh function.
cacheObjectCache control function: get(url<String>), has(url<String>), clear()

useQueryEvent hook return an object with following keys:

NameTypeDescription
sendRequestFunctionTrigger function to send request
isSendingBooleanState of sending status
dataAnyData returned from request
errorError or nullRequest error.
loadingBooleanRequest loading state.
mutateFunction(data<ReactState>)Mutate function to manipulate data state, available everywhere inside QueryProvider.
refreshFunctionData refresh function.
cacheObjectCache 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
2.0.1

1 year ago

2.0.0

1 year ago

1.2.0

1 year ago

1.1.6

1 year ago

1.2.2

1 year ago

1.2.1

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.9

1 year ago

1.1.5

1 year ago

1.1.4

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.0.2

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago