2.2.4-1 • Published 6 months ago

frhooks v2.2.4-1

Weekly downloads
-
License
ISC
Repository
github
Last release
6 months ago

FRHooks React Js

Hook untuk membantu CRUD

Installation

Install with npm

  npm install frhooks

Install with Yarn

  yarn add frhooks
required:
  • yup
  • axios

Hooks

ApiRoute

apiRoute()\ .headers({...headers})\ - set header\ .params({...params})\ -set params url\ .get(resp => resp, {...config})\ -request GET\ .destroy(resp => resp, {...config})\ -request DELETE\ .data({...data})\ -set data (method POST | PUT)\ .sendJson(method, resp => resp, {...config})\ -mengirim data dalam bentuk json ("application/json")\ .sendUrlEncode(method, resp => resp, {...config})\ -mengirim data dalam bentuk query\ ("application/x-www-form-urlencoded")\ .sendFormData(method, resp => resp, {...config})\ -mengirim data dalam bentuk query ("multipart/form-data")\ .link()\ -generate url

Usage/Example

=> Buat url dalam 1 object\ => apiRoutes.js

const apiRoutes = {
  project: {
    index: '/project',
    detail: '/project/:id'
  },
  ...other
}
export default apiRoutes

=> Jika Menggunakan Typescript\ => Buat Typescript untuk url

declare namespace FRHooks {
  type Route = typeof import(".../apiRoutes").default
  type ApiRoute = {
    [P in keyof Route]: (
      path: keyof Route[P],
      arg?: Record<string, string>
    ) => Api;
  };
}

=> Daftar Provider\ => Buat instance dari Axios

import { HookProvider } from 'frhooks';
import axios from 'axios';
import apiRoutes from '../apiRoutes'

const api = axios.create({
  baseUrl: 'http://localhost:8000/api'
})

<HookProvider client={api} route={apiRoutes}>
  <App/>
</HookProvider>

=> Cara Menggunakan ApiRoute\ => Buat instance dari Axios\ => GET, POST, PUT, DELETE

import React FRHooks from 'react'
import {apiRoute} from 'frhooks'

function App(){
  const fetchData = async () => {
    data = await apiRoute().project('index').get()
  }
  
  const fetchDetail = async (id) => {
    data = await apiRoute().project('detail', {id}).get()
  }

  const postData = async () => {
    const data = await apiRoute()
          .project('index')
          .data({name:'Project A', description: ''})
          .sendJson('POST')
  }

  const destroyData = async () => {
    data = await apiRoute().project('detail', {id: 1}).destroy()
  }
}

useMutation

Manipulasi Form

Props

PropsFungsi
defaultValue*inialisasi nilai awal
schemaschema validasi attribut menggunakan Yup
formatmemberikan format ketika inputan
scenariomembagi validasi bedasarkan scenario

Function you can use

NoObjectTypeFungsi
1loadingbooleanIndikator saat request GET
2processingbooleanIndiator saat POST, PUT, DESTROY
3dataTData
4errorsObjectMenampilkan error saat validasi
5isNewRecordbooleanInditator untuk data baru
6setData(value)functionFungsi untuk set data
7clearData(except:[])functionMembersihkan data tambahkan key jika ingin mengeluarkan data yang tidak ingin di bersihkan
8increment(value)functionFungsi untuk manaikan nilai
9decremet(value)functionFungsi untuk menurunkan nilai
10reformat(cb(value))functionReformat data
11validate(key, scenario)booleanMendapatkan validasi berdasarkan key
12fails(scenario)functionMelakukan validasi manual
13setError({...error})functionSet manual error
14clearError()functionMembersihkan Error validasi
15error(key)functionMendapatkan Error berdasarkan key
16message(key)stringMendapatkan pesan error berdasarkan key
17isValid()functionIndikator untuk melihat validasi valid
18merge(values)functionMenggabungkan data
19register(key, defaultValue, options)functionMendaftarkan Attribute input
20postfunctionAksi POST
21putfunctionAksi PUT
22destroyfunctionAksi DELETE
23getfunctionAksi GET
24cancel()functionMembatalkan request axios
25addItem(Key, {value}, position)functionMenambahkan value baru kedalam array
26setItem(value)functionMengubah nilai berdasarkan key
27getItem(Key, defaultValue)functionMendapatkan nilai berdasarkan key
28removeItem(key, index)functionMenghapus value dari array berdasarkan index
29itemValidboolean
30setDispatch()function
31validateItem(key)booleanMendapatkan validasi array berdasarkan index dan field
32attributesarrayMendapatkan attribute atau field yand telah daftarkan
33incrementItem(value)functionFungsi untuk manaikan nilai
34decrementItem(value)functionFungsi untuk menurunkan nilai

Usage/Example

// app.js
import React from 'react'
import {useMutation, useSelector} from 'frhooks'

const App = () => {
 const form = useMutation({
    defaultValue: {
      id: 0,
      title: "",
      body: "",
      items:[]
    },
    
    // Optional validasi
    schema: (yup: any) =>
      yup.object().shape({
        title: yup.string().required().label("Title"),
        body: yup.string().required().label("Description"),
        //Contoh Array
        items:yup.array().of(
            y.object().shape({
              label: y.string().required().label("Label"),
            })
          )
          .required()
      }),
      
    // Optional kondisi
    isNewRecord: (data) => data.id === 0,
    
    // Optional Scenario
    // Memecah field saat validasi berdasarkan scenatio
    scenario: {
      create: ["title"],
      update: ["title", "body"],
      step1: ["title"],
      step2: ["items"]
    },
    
    // Optional Format
    // Optional format saat input
    format: {
      title: (value) => value.trim(),
    },
    
    // Optional Tambah Key unik untuk mendaftarkan ke daftar CONTEXT
    key: 'key_context'
  });

// Contoh mengirim data POST | PUT
  const onSubmit = () => {
   // Note: ['/example/:id', {id}] gunakan ini jika butuh nilai
    form.post('/example', {
        // Optional default: post
        // Note: form.put(url, options) jika ingin menggunakan PUT
        method: "post",
        
        // Optional validasi, default false
        // Note: Pastikan schema telah ditambahkan
        validation: true, 
        
        // Optional scenario, default: false
        // Note: Pastikan scenario telah ditambahkan
        scenario: "create",
        
        // Optional send post tanpa data, default: false
        useEmpty: false,
        
        // Optional send post tanpa data yang di filter
        except: [],
        
        // Optional send post hanya data yang di filter
        only: [],
        
        onBeforeSend: () => {
            console.log('action before send')
        },
        
        onSuccess: (data, jhqx) => {
            console.log('action on success', data)
        },
        onError:(e) => {
            console.log('error', e)
        },
        onAlways: () => {
            console.log('action on always')
        },
        options: {
          headers: {
             //default; application/json
             //Note: Ubah bagian ini jika ingin mengirim data menggunakan Form Data, useMutation akan mengubah secara otomatis object => form-data 'content-type': 'multipart/form-data',
             //Note: Ubah bagian ini jika ingin mengirim data menggunakan urlencode, useMutation akan mengubah secara otomatis object => query url encode 'content-type': "application/x-www-form-urlencoded",
              ...(other options axios config)
          }  
        },
         ...(other options)
    })
  }
  
  // Contoh Delete
    const onDelete = () => {
        // Note: ['/example/:id', {id}] gunakan ini jika butuh nilai
        form.destroy('/example', {
            onSuccess: (data, jhqx) => {
                console.log('action on success', data)
             },
            ...(other options)
        })
    }
    
 // Contoh GET
    const get = () => {
        // Note: ['/example/:id', {id}] gunakan ini jika butuh nilai
        form.get('/example', {
            onSuccess: (data, jhqx) => {
                console.log('action on success', data)
              },
             ...(other options)
        })
    }
    
    const change={(e) => {
        form.setData({title: e.target.value})
    }
    
    //Membuat validasi saat blur
    cost blur={() => {
        form.validate('title')
    }
    
    //Contoh Manual Validasi
    const validation = async () => {
        //Normal
        const invalid = await form.fails();
        //Dengan scenario
        const invalid = await form.fails('step1')
        //Dengan Schema
        const invalid = await form.fails((yup) => yup.object().shape({...schema}))
        
        if(invalid) {
            console.log(form.errors)
        }
    }
    
    //Contoh Manual Validasi Array
    const validationArray = async () => {
        //Normal
        const invalid = await form.validateItem('items');
        if(invalid) {
            console.log(form.errors)
        }
    }
    
     const addNew = () => {
        form.addItem('items', {label: ''}, 'start' | 'end'| number of index)
    }
    
    //Note: Hapus Array
    const removeItem = (i) => () => {
        form.removeItem('items')
        //atau
        form.removeItem('items', i)
        //atau
        form.removeItem('items', (v) v.label === 'test') 
    }

  render <div>
    {form.loading ? "Memuat Data..." : JSON.stringify(mutation.data)}

    <div>
      <input value={form.data.title} onChange={change} onBlur={blur}/>
      //Atau
      <input {...form.register('name',"",  {...options})}/>
      {form.error('title') ? <span>{form.message('title')}</span> : null}
    </div>

    <button onClick={validation} >validasi</button>
    <button disabled={!form.isValid()} onClick={onSubmit}>
        {form.processing ? "loading..." : "simpan"}
    </button>
  </div>
}

=> Jika Array

const App = () => {
    ...(form)
    return <div>
        <ul>
        {
            form.data.items.map((value, i) => (
                <li key={i}>
                <input 
                value={form.getItem('items.0.label', '')} 
                onChange={(e) => {
                    form.setItem( {['items.0.label']: e.target.value})
                }} 
                onBlur={() => {
                    form.validateItem('items.0.label')
                }}
                />
                {form.error('items.0.label') ? <span>{form.message('items.0.label')}</span> : null}
                <button onClick={removeItem(i)}>- Remove Array</button>
                </li>
            )
        )}
    </ul>
    
    <button onClick={validationArray} >validasi array</button>
    <button onClick={addNew}> + Add New Array</button>
    </div>
}

=> Jika Nested Array dan Seterusnya

const App = () => {
    ...(form)
    return <div>
        <ul>
        {
            form.data.items.map((value, i) => value.items2.map((value2, i2) => (
               <li key={i}>
                <input 
                value={form.getItem(`items.${i}.items2.${i2}.label`, '')} 
                onChange={(e) => {
                    form.setItem( {[`items.${i}.items2.${i2}.label`]: e.target.value})
                }} 
                onBlur={() => {
                    form.validateItem(`items.${i}.items2.${i2}.label`)
                }}
                />
                {form.error(`items.${i}.items2.${i2}.label`) ? <span>{form.message(`items.${i}.items2.${i2}.label`)}</span> : null}
                <button onClick={() => {
                  form.removeItem(`items.${i}.items2`, i2)
                }}>- Remove Array</button>
                </li>
            ))
               
            )
        }
    </ul>
    
    <button onClick={() => {
      form.validateItem(`items.${i}.items2`)
    }} >validasi array</button>
    <button onClick={() => {
      form.addItem(`items.${i}.items2`, {label: ''})
    }}> + Add New Array</button>
    </div>
}

useValidation

Menggunakan validasi schema (Yup)

Props

PropsFungsi
schema*schema validasi attribut menggunakan Yup
scenariomembagi validasi bedasarkan scenario
valueData

Function you can use

NoPropsTypeFungsi
1errorsobjectHasil validasi
2scenariostringMendapatkan scenario
3setData({})functionset data
4setError({})functionset error manual
5fails({scenario, value})functionMelakukan validasi
6validate(field, scenario)booleanMelakukan validasi per field
7clearError()functionMenghapus error
8error(fields)booleanMendapatkan indikasi error per field
9message(fields)booleanMendapatkan pesan error per field
10validbooleanMendapatkan indikasi error
11validateItem(key, attr, index, scenario)booleanMelakukan validasi item array
12errorItem(key, attr, index)booleanMendapatkan indikasi error item array
13messageItem(key, attr, index)stringMendapatkan pesan error item array

Usage/Example

// app.js
import React from 'react'
import {useValidation} from 'frhooks'
const App = () => {
 const form = useValidation({
    // Optional
    // value tambahkan value disini untuk validasi bersarkan perubahan data
    // form.setData(): jika value ditambahkan maka pada fungsi ini bisa untuk tidak digunakan
    value: data,
    schema: (yup: any) =>
      yup.object().shape({
        title: yup.string().required().label("Title"),
        body: yup.string().required().label("Description"),
        items: y
          .array()
          .of(
            y.object().shape({
              text: y.string().required().label("test"),
            })
          )
          .required(),
      }),

    // Optional Scenario
    // Memecah field saat validasi berdasarkan scenatio
    scenario: {
      create: ["title"],
      update: ["body"],
      //Experimental jika ingin memvalidasi hanya array saja
      //example: form.fails({scenario: "items"})
      validationItems: ["items"]
    },
  });
 
    const validation = async () => {
        //Normal
        const invalid = await form.fails();
        //Dengan scenario
        const invalid = await form.fails({scenario: "create"})
        //Dengan value
        const invalid = await form.fails({value: {...(new value)})
        if(invalid) {
            console.log(form.errors)
        }
    }
    return "Contoh sama dengan useMutation bukan Array"
}

=> Jika Array

    <ul>
        {array.map((v, i) => ( <div key={i}>
            <input onBlur={() => form.validateItem('items', 'text', i)} />
            {form.errorItem('items', 'text', i) ? <span>{form.messageItem('items', 'text', i)}</span> : null}
        </div>)}
    </ul>

useServerValidation

Menggunakan validasi asyncronus API

Props

PropsFungsi
urlAlamat api yang dituju
selector:(resp) => respMendapatkan data error dengan normal response < 400 Http Code
options: {key:(param, locale)}Mengatur object error
param: {type, path}Mendapatkan object error
fieldobjectMemberi pengaturan invidividual
callback(err)Mendpatkan balikan hasil error

Function you can use

NoPropsTypeFungsi
1processingbooleanMendapatkan indikasi proses
2errorsobjectMendapatkan error
3serve({url, key, only, method, data, cb})functionMelakukan validasi
4loadingboolenMendapatkan indikasi proses
5validate(key, data, options)booleanMelakukan validasi berdasarkan key

Usage/Example

=> Contoh Error Response Api

//validation.json
error: [
    {path: "title", type: "required"},
    {path: "phoneNumber", type: 'exist'},
    {path: "body", type:"minLength", length: 8}
]

atau 
// path dan type diubah menjadi field dan rule
error: [
  {field: "title", rule: "required"},
  ....
]

atau

error:{
  code: 5,
  name: "is exist"
}
// app.js
import React from 'react'
import {useServerValidation} from 'frhooks'
const App = () => {
 const {server, errors} = useServerValidation({
    url: "http://localhost/validation",
    // Mendapatkan error dari API
    selector: (resp) => resp.error,
    option: {
    //Note: Normal 
    required: ({path}) => `${path} is required`,
    //Note: with locale
    required: ({path}, {r, t}) =>r("required", { path: t(path) }, "validation"),

    exist: ({path}) => `${path} already exist`,
    minLength:({path, length}) => `${path} minimum length is ${length}`,
    
    //Note: jika param diubah 
    required: ({field}) => `${path} is required`,
    },

   // Optional
   // dapat diubah
    param:{
        path: "path",// default path,
        type: "type",// default type
    }
    field: {
        name: {
          url: "exmple.json",
          selector: (resp) => {
            // Balikan berupa string˝
            return resp.data.error.name
          }
        }
    },
    // Optional
    calback: (err) => {
        form.setError(err)
    }
   
  });
 
    const validation = async () => {
      const valid = await  serve({method: "post", ...(other optional options)})
      if(!valid){
          console.log(errors)
      }
    }
    
    //Note: Kombinasi dengan useMutation
    const onSubmit = () => {
        form.post('/example', {
        // Optional validasi, default false
        validation: true,
        serverValidation: {
            serve: serve,
            method: "post",
        },
         ...(other options)
    })
    }
    return <>{JSON.stringify(errors)}<>
}

useTable

Manipulasi Table

Props

PropsFungsi
selector*Mendapatkan data dari respon API
total*Mendapatkan jumlah data dari respon API
disabledOnDidMountMematikan aksi saat didmount
configKonfigurasi AxiosRequestConfig
sorturutan
paginationpaginasi
keyMenambahkan data kedaftar CONTEXT

Function you can use

NoObjectTypeFungsi
1loadingbooleanIndikator loading saat GET
2totalnumberMenampilkan total
3dataTData
4orderBystringMenampilkan key urutan
5orderasd - descMenampilkan type urutan
6query(key, defaultValue)functionMendapatkan hasil set query
7setTotal(value)functionSet total secara manual
8setQuery({value})functionSet query
9onOrder(key)functionSorting berdasarkan key
10clear()functionMembersihkan query
11clearExcept([])functionMembersihkan query dengan parameter
12clearOnly([])functionMembersihkan query dengan parameter
13remove()functionMembersihkan Error validasi
14reload()functionMemuat ulang
15remove(key, resetPage)functionMenghapus query berdasarkan key dan reset page = defaultPage
16add(value, "start" or "end" or number)functionMenambahkan data baru kedalam list
17update(index or (v) => boolean, value)functionMemperbaharui data list berdasarkan Index
18destroy(index or (v) => boolean)functionMenghapus data darri list berdasarlkan Index
19isEmptybooleanMendapatkan indikasi data kosong
20has(key)booleanMendapatkan inidikasi dari key query
21setDispatch()function(key: 'list_test') diset untuk mendaftarkan data kedalam context
22paginationObject...
23register()function...

Pagination Function you can use

ObjectTypeFungsi
pagenumberMendapatkan page
fromnumberMendapatkan nomor urutan pertama
tonumberMendapatkan nomor urutan terakhir
lastPagenumberMendapatkan nomor page terakhir
perPageOptionsnumber[]Mendapatkan daftar size page tersedia
perPagenumberMendapatkan nilai size page
setPage(value)functionSet page manual
setPerPage(value)functionSet size page manual
nextButton()function...
backButton()function...
firstButton()function...
lastButton()function...
onPerPageChangefunctionfungsi untuk set nilai size page
textstringMendapatkan summary data

Usage/Examples

// list.json
//exmple API response
 {
  status: 200,
  data: [...],
  total: 10
 }
// app.js
import React from 'react'
import {useTable} from 'frhooks'
const App = () => {
  //Note: ["/examples/:path", {path: "test"}] bila ingin menambahkan  path
  const table = useTable("/examples", {
    // Mendapatkan data dari response API
    selector: (resp) => resp.data, 
    // Mendapatkan nilai total dari response API
    total: (resp) => resp.total,
    // Optional 
    // Note: Cara merubah params sorting
    // Example: localhost/example?order=asc&orderby=id
    sort: {
      params: {
        order: "order",
        orderBy: "orderBy",
      },
      order: "desc", // Note: default order, default: desc
      orderBy: "id", // Note: default orderBy, default: id
    },
    // Optional
    // Note: Melakukan custome terhadap pagination
    pagination: {
      //Example: localhost/example?page=1&perPage=10
      params: {
        page: "page",
        perPage: "perPage",
      },
      startPage: 1,// default page, default: 1 or 0
      perPage: 10,// default perPage, default: 10,
      perPageOptions: [5, 10, 15, 25],
      // formula mendapatkan nilai from
      from: (total, page, size, df) => page - 1 + (df === 0 ? 1 : 0)) * size +
      (total === 0 ? 0 : df === 0 ? 1 : df),
       // formula mendapatkan nilai to
      to: (total, page, size, df) =>  Math.min(total, (page + (df === 0 ? 1 : 0)) * size),
      //Mendapatkan nilai page terakhir
      lastPage: (total, size) => Math.max(0, Math.ceil(total / size)), // formula
      disableFirst: (total, page, df) => total !== 0 && page === df,
      disableLast: (total, page, lp) => total !== 0 && page === lp,
    },
    // Optional mendaftarkan hasil ke CONTEXT
    key: 'key_context',
    //tambahkan true jika tidak ingin menjalan saat load page, default: false
    disabledOnDidMount: false 
  })
  
  render <div>
      <div>
      <input type="text" value={table.query('title', '')} onChange={e => table.setQuery({title: e.target.value})} />
      //atau
      <input type="text" {...table.register("title", "")} />
      
          {table.pagination.text}
          <button {...table.pagination.firstButton()}>prev</button>
          <button {...table.pagination.backButton()}>prev</button>
          {table.pagination.page}
          <button {...table.pagination.nextButton()}>next</button>
          <button {...table.pagination.lastButton()}>next</button>

        <button onClick={() => table.onOrder("name")}>
            {table.order}-{table.orderBy}
        </button>
        <button onClick={table.clear}>Clear</button>
        <button onClick={table.reload}>Reload</button>
     </div>
     
      <table>
        <thead>
          <tr>
            <td>No</td>
            <td>Title</td>
          </tr>
        </thead>

        <tbody>
          {table.loading ? 'loading...' : null}
          
          {table.data.map((v: any, i) => (
            <tr key={i}>
              <td>{table.data.from + (i+1)}</td>
              <td>{v.title}</td>
            </tr>
          ))}
        </tbody>
      </table>
  </div>
}

useFetch

fetching data

Props

PropsFungsi
selector*Mendapatkan data dari respon API
defaultValueNilai default untuk data
defaultParamsNila default untuk query
disabledOnDidMountMematikan aksi saat didmount
configKonfigurasi AxiosRequestConfig
debounceTimeNilau debounce saat setQuery
depsDepedencies
getData(data)Mendapatkan Data secara manual
keyMenambahkan data kedaftar CONTEXT

Function you can use

NoObjectTypeFungsi
1loadingbooleanIndikator loading saat GET
2totalnumberMenampilkan total
3dataTData
4errorobjecterror query
5getQuery(key, defaultValue)functionMendapatkan hasil set query
6setQuery({value})functionSet query
7clear({except, only})functionMembersihkan query
14refresh()functionMemuat ulang
16add(value, "start" or "end" or number)functionMenambahkan data baru kedalam list (jika data dalam bentuk array)
17update(index or(v) => number, value)functionMemperbaharui data list berdasarkan Index (jika data dalam bentuk array)
18destroy(index or (v) => number)functionMenghapus data dari list berdasarkan Index (jika data dalam bentuk array)
19isEmptybooleanMendapatkan indikasi data kosong
20has(key)booleanMendapatkan indikasi dari key query
21setDispatch()function(key: 'list_test') diset untuk mendaftarkan data kedalam context

Usage/Examples

// list.json
//exmple API response
 {
  status: 200,
  data: [...],
 }
 // atau 
 {
  status: 200,
  data: {...},
 }
// app.js
import React from 'react'
import {useFetch} from 'frhooks'
const App = () => {
  //Note: ["/examples/:path", {path: "test"}] bila ingin menambahkan  path
  const project = useFetch("/examples", {
    // Mendapatkan data dari response API
    selector: (resp) => resp.data, 
    // Mendapatkan nilai total dari respon API
    // Tentukan nilai awal jika array maka [] atau {}
    defaultValue: {},
    // Optional
    // Optional mendaftarkan hasil ke CONTEXT
    key: 'key_context',
    // Optional
    // Tambahkan true jika tidak ingin menjalan saat load page, default: false
    disabledOnDidMount: false ,
    // Optional
    // Jika ingin mendaptkan data secara manual
    getData: (value) => {
        console.log(value, 'data in here')
    }
    ...(other options)
  })
  
  render "Lebih kurang mirip useTable"
}

useDispatch

useSelector

context

Function you can use

ObjectTypeFungsi
dispatch(key, value)functionset data object atau list berdasarkan key
clearMutation('key')functionBersihkan mutasi
clearList('key')functionBersihkan list

Usage/Examples

=> Jika menggunakan Typescript maka tambahkan ini

//index.d.ts override

declare namespace FRHooks {
  type DataContext = {
    user: {
      name: string;
    };
  };
}
// app.js
import React from 'react'
import {useDispatch, useSelector} from 'frhooks'

const App = () => {
    const {dispatch, clearMutation, clearList} = useDispatch()
    // atau
    const {dispatch} = useDispatch('key', {
     // Optional, default mutation
     // Note: Jika array maka ubah menjadi "list"
     type: 'mutation',
     // Optional hanya untuk bertipe list
     total: 0, 
     defaultValue: {
       name: ''
     }
   })

  const result = useSelector(['key']) 
  // atau modifikasi respon
  const result = useSelector(['key'], (data) => data) 
  
 React.useEffect(() =>{
  dispatch('key', {name: 'test'})
 }, [])

  render <div> 
  {JSON.stringify(result)}
  </div>
}

useLang

Multi Bahasa

Function you can use

ObjectTypeFungsi
t()functionMendapatkan hasil terjemah bahasa
r()functionMemberi nilai berdasarkan param
setLang()functionMenentukan bahasa digunakan

Usage/Examples

// en.json => pastikan nama file benar "${en}".json
// default json
// ini bisa digunakan untuk memodifikasi pesan validasi useMutation
 {
  "attribute": {
    "name": "Name"
  },
  "message": {
    "exampleMessage": "Hello Word",
    "exampleReplaceMessage": "Hello :name"
  },
  "validation": {
    "default": ":path is invalid",
    "required": ":path is a required field",
    "oneOf": ":path must be one of the follwing values: :values",
    "notOneOf": ":path must not be one of the following values: \":values\"",
    "defined": ":path must be defined",
    "stringLength": ":path must be exactly :length characters",
    "stringMin": ":path must be at least :min characters",
    "stringMax": ":path must be at most :max characters",
    "matches": ":path must match the following: \":regex\"",
    "email": ":path must be a valid email",
    "url": ":path must be a valid URL",
    "uuid": ":path must be a valid UUID",
    "trim": ":path must be a trimmed string",
    "lowercase": ":path must be a lowercase string",
    "uppercase": ":path must be a upper case string",
    "numberMin": ":path must be greater than or equal to :min",
    "numberMax": ":path must be less than or equal to :max",
    "lessThan": ":path must be less than :less",
    "moreThan": ":path must be greater than :more",
    "positive": ":path must be a positive number",
    "negative": ":path must be a negative number",
    "integer": ":path must be an integer",
    "dateMin": ":path field must be later than :min",
    "dateMax": ":path field must be at earlier than :max",
    "isValue": ":path field must be :value",
    "noUnknown": ":path field has unspecified keys: :unknown",
    "arrayMin": ":path field must have at least :min items",
    "arrayMax": ":path field must have less than or equal to :max items",
    "arrayLength": ":path must have :length} items"
  }
}
// id.json => pastikan nama file benar "${id}".json
// ini bisa digunakan untuk memodifikasi pesan validasi useMutation

 {
  "attribute": {
    "name": "Nama",
    "mandatory": "Wajib"
  },
  "message": {
    "exampleMessage": "Hallo Dunia",
    "exampleReplaceMessage": "Hallo :name"
  },
  "validation": {
    "default": ":path tidak valid",
    ...dll
  }
}

//tsconfig
{
  "compilerOptions": {
    "resolveJsonModule": true,
  }
}
// index.d.ts override*
declare namespace FRHooks {
  type AttributeKey =
    | keyof typeof import("../lang/en.json")["attribute"]
    | Array<keyof typeof import("../lang/en.json")["attribute"]>;

  type AttributeMessage =
    | keyof typeof import("../lang/en.json")["message"]
    | Array<keyof typeof import("../lang/en.json")["message"]>;

    type Lang = "en" | "id"
}
import { HookProvider } from 'frhooks';

const language = {
  lang: 'en',
  // folder berisikan file json, en.json, id.json, ...dll
  path: './lang/', 
  fallback: 'en',
  // Optional tambah jika ingin memeliki nilai awal
  default: {...default},
  // Optional
  loading: true,
}

// index.js
<HookProvider language={language}>
  <App/>
</HookProvider>

// app.js
import React from 'react'
import {useLang} from 'frhooks'

const App = () => {
  const {t, r, setLang} = useLang()
  
 React.useEffect(() =>{
    setLang('id') // Set Bahasa
 }, [])

  render <div> 
  <p>{t('name') || t(['name', 'mandatory'])}</p>
  <p>{r('exampleReplaceMessage', {name: 'BUDI'})}</p>
  </div>
}
2.2.4-1

6 months ago

2.2.3-5

9 months ago

2.2.3-4

9 months ago

2.2.3-3

9 months ago

2.2.3-2

9 months ago

2.2.3-9

8 months ago

2.2.3-8

8 months ago

2.2.3-7

9 months ago

2.2.3-6

9 months ago

2.2.3-1

9 months ago

2.2.4-0

8 months ago

2.2.1-7

10 months ago

2.2.1-6

10 months ago

2.2.1-5

10 months ago

2.2.1-4

10 months ago

2.2.1-9

10 months ago

2.2.1-8

10 months ago

2.2.1-3

10 months ago

2.2.1-2

10 months ago

2.2.1-1

10 months ago

2.2.3-0

10 months ago

2.2.2-6

10 months ago

2.2.2-5

10 months ago

2.2.2-4

10 months ago

2.2.2-3

10 months ago

2.2.2-9

9 months ago

2.2.2-8

9 months ago

2.2.2-7

9 months ago

2.2.2-2

10 months ago

2.2.2-1

10 months ago

2.2.2-0

10 months ago

2.2.1-0

11 months ago

2.2.0-8

11 months ago

2.2.0-9

11 months ago

2.2.0-7

1 year ago

2.2.0-6

1 year ago

2.2.0-5

1 year ago

2.2.0-4

1 year ago

2.2.0-3

1 year ago

2.2.0-2

1 year ago

2.2.0-1

1 year ago

2.1.4-beta-0

1 year ago

2.1.4-beta-2

1 year ago

2.1.4-beta-1

1 year ago

2.1.4-beta-8

1 year ago

2.1.4-beta-7

1 year ago

2.1.4-beta-9

1 year ago

2.1.4-beta-4

1 year ago

2.1.4-beta-3

1 year ago

2.1.4-beta-6

1 year ago

2.1.4-beta-5

1 year ago

2.0.3

1 year ago

2.0.2

1 year ago

2.0.5

1 year ago

2.0.4

1 year ago

2.0.7

1 year ago

2.0.6

1 year ago

2.0.9

1 year ago

2.1.4-1

1 year ago

2.0.1

1 year ago

2.0.0

1 year ago

2.1.4-3

1 year ago

2.1.4-2

1 year ago

2.1.8-1

1 year ago

2.1.4-5

1 year ago

2.1.8-0

1 year ago

2.1.4-4

1 year ago

2.1.8-3

1 year ago

2.1.4-7

1 year ago

2.1.8-2

1 year ago

2.1.4-6

1 year ago

2.1.8-5

1 year ago

2.1.4-9

1 year ago

2.1.8-4

1 year ago

2.1.4-8

1 year ago

2.1.8-7

1 year ago

2.1.8-6

1 year ago

2.1.8-9

1 year ago

2.1.8-8

1 year ago

2.1.4-beta-1.9

1 year ago

2.1.4-beta-1.8

1 year ago

1.9.1

1 year ago

1.9.0

1 year ago

2.1.4-beta-1.1

1 year ago

2.1.4-beta-1.3

1 year ago

2.1.4-beta-1.2

1 year ago

2.1.4-beta-1.5

1 year ago

2.1.4-beta-1.4

1 year ago

2.1.4-beta-1.7

1 year ago

2.1.4-beta-1.6

1 year ago

2.1.9-0

1 year ago

2.1.9-2

1 year ago

2.1.9-1

1 year ago

2.1.9-4

1 year ago

2.1.9-3

1 year ago

2.1.9-6

1 year ago

2.1.9-5

1 year ago

2.1.9-8

1 year ago

2.1.9-7

1 year ago

2.1.9-9

1 year ago

1.8.2

1 year ago

1.8.1

1 year ago

1.8.0

1 year ago

1.9.9

1 year ago

1.9.8

1 year ago

1.9.7

1 year ago

1.9.6

1 year ago

1.9.5

1 year ago

1.9.4

1 year ago

1.9.3

1 year ago

1.9.2

1 year ago

2.1.2

1 year ago

2.1.1

1 year ago

2.1.4

1 year ago

2.1.3

1 year ago

2.1.0

1 year ago

2.2.0-0

1 year ago

1.8.9

1 year ago

1.8.8

1 year ago

1.8.7

1 year ago

1.8.5

1 year ago

1.8.4

1 year ago

1.8.3

1 year ago

1.6.4

1 year ago

1.6.3

1 year ago

1.6.2

1 year ago

1.6.1

1 year ago

1.6.0

1 year ago

1.7.9

1 year ago

1.7.8

1 year ago

1.7.7

1 year ago

1.5.9

1 year ago

1.7.6

1 year ago

1.5.8

1 year ago

1.7.5

1 year ago

1.5.7

1 year ago

1.7.4

1 year ago

1.5.6

1 year ago

1.7.3

1 year ago

1.5.5

1 year ago

1.7.2

1 year ago

1.5.4

1 year ago

1.7.1

1 year ago

1.5.3

1 year ago

1.7.0

1 year ago

1.5.2

1 year ago

1.5.1

1 year ago

1.6.9

1 year ago

1.6.8

1 year ago

1.6.7

1 year ago

1.6.6

1 year ago

1.6.5

1 year ago

1.5.0

1 year ago

1.4.9

1 year ago

1.4.8

1 year ago

1.4.7

1 year ago

1.4.6

1 year ago

1.4.5

1 year ago

1.4.4

1 year ago

1.4.3

1 year ago

1.4.2

1 year ago

1.4.1

1 year ago

1.4.0

1 year ago

1.3.9

1 year ago

1.3.8

1 year ago

1.3.7

1 year ago

1.3.6

1 year ago

1.3.5

1 year ago

1.3.4

1 year ago

1.3.3

1 year ago

1.3.2

1 year ago

1.3.1

1 year ago

1.3.0

1 year ago

1.2.9

1 year ago

1.2.8

1 year ago

1.2.7

1 year ago

1.2.6

1 year ago

1.2.5

1 year ago

1.2.4

1 year ago

1.2.3

1 year ago

1.2.2

1 year ago

1.2.1

1 year ago

1.2.0

1 year ago

1.1.9

1 year ago

1.1.8

1 year ago

1.1.7

1 year ago

1.1.6

1 year ago

1.1.4

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.9

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.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago