0.1.5 • Published 7 months ago

@coding4tomorrow/c4t-next v0.1.5

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

Coding 4 Tomorrow Next

TODO

  • Fix errors showing up during tests. They're not blocking and don't fail the test but something might not be right.

Installation

yarn add @coding4tomorrow/c4t-next

or

npm install @coding4tomorrow/c4t-next --save

Getting started

To use the library you'll have to call the useSetup hook first thing when your React app is loaded.

_app.js for NextJS

// complete configuration
import { useSetup } from '@coding4tomorrow/c4t-next'

const App = ({ Component, pageProps }) => {
  useSetup({
    api: {
      baseURL: 'http://localhost:5000',
      routes: apiRoutes,
    },
  })

  return <Component {...pageProps} />
}

export default App

You can check all the available options for useSetup below.

Examples

You can find some examples in the folder /examples of the project.

API Documentation

useSetup

This hook configure the whole library.

The main parts are:

  • API calls
  • Authentication (cookies, tokens)
  • Automatic token refresh when expired
  • Axios headers configuration (yes we use Axios under the hood).

Hook options

import apiRoutes from './apiRoutes'

useSetup({
  api: {
    // required, this is the base URL of the API
    baseURL: 'http://localhost:4000',
    // required if you want to use the API
    routes: apiRoutes,
    // optional, defaults to 'auth.jwt.refresh'
    refreshTokenRoute: 'auth.jwt.refresh',
    // optional, defaults to 'auth.login'
    loginRoute: 'auth.login',
    // optional, defaults to 'users.me'
    meRoute: 'users.me',
  },
  auth: {
    // optional, defaults to '/login'
    loginPageRoute: '/login',
    // optional, defaults to 'c4t_token'
    tokenCookieName: 'c4t_token',
    // optional, defaults to 'c4t_refresh_token'
    refreshTokenCookieName: 'c4t_refresh_token',
  },
  axios: {
    // optional
    headers: {
      // custom headers which will be appended on each request
      'X-API-Key': '...',
    },
  },
})

apiRoutes

This is where all the API routes are defined.

You can then use them in combinaison with useSWR or useApi or useFormApi etc...

const apiRoutes = ({ get, post, patch, put, del }) => ({
  users: {
    me: get('/v1/users/me'),
  },
  books: {
    getAll: get('/v1/books'),
    delete: del('/v1/books/:id'),
    update: patch('/v1/books/:id'),
    override: put('/v1/books/:id'),
    create: post('/v1/books'),
  },
  auth: {
    login: post('/v1/auth/login'),
    refreshToken: post('/v1/auth/refresh-token'),
  },
})

export default apiRoutes

useSWR

This hook is an implementation of swr (https://swr.vercel.app/docs/getting-started) to work with our api system, so we keep all the benefits of using swr while having an easy api integration system.

It's useful when doing a simple request that has to be executed right away.

// apiRoutes.js
const apiRoutes = ({ post }) => ({
  books: {
    get: get('/books/:id'),
  },
})

// useSWRExample.js
import { useSWR } from '@coding4tomorrow/c4t-next'

//...
const { data: book, isLoading } = useSWR('books.get', { id: 1 })

if (isLoading) {
  return <p>Loading...</p>
}

return (
  <div>
    <h2>{book.title}</h2>
    <h3>{book.author}</h3>
    <p>{book.description}</p>
  </div>
)

Hook options

const {
  data, // the loaded data
  error, // the error if there is
  isLoading, // used to
  mutate, // used to mutate the cached version of the resource
} = useSWR(apiRoute, params, options)

data

data is null until loaded.

If data had results before a second call, it'll keep its current value until the new value is loaded.


error

error holds the error returned from the call, if there is.

data will equal null if there's an error.


isLoading

While isLoading is true, data is null.


apiRoute

This is the route path to our API call, for example books.getAll


params

This is the params for the apiRoute, for example if your URL is: /v1/books/:id you'll have to pass { id: 1 } to transform it to /v1/books/1.


options

This is the options of swr.

More information at https://swr.vercel.app/docs/options#options


useApi

Sometimes you need to defer the api request, for example to send information after a click.

This is where useApi comes in play.

// apiRoutes.js
const apiRoutes = ({ post }) => ({
  books: {
    like: post('/books/:id/like'),
  },
})

// useApiExample.js
import { useApi } from '@coding4tomorrow/c4t-next'

//...
const { request, loading } = useApi('books.like', { id: 1 })

return (
  <div>
    <a
      onClick={() => {
        const [err, response] = request({
          fieldName: 'some field infos',
        })

        if (err) {
          // display error somewhere
          return
        }

        if (response.success) {
          // show feedback to the user
        }
      }}
    >
      Like
    </a>
  </div>
)

useFormApi

This hook is useful when working with Ant Design forms.

It's taking care of:

  • API call on submission
  • Automatic error handling based on fields name

Simple example:

import { useFormApi } from '@coding4tomorrow/c4t-next'
import { useForm, Form, Input, Button } from 'antd'

const [form] = useForm()

const { ErrorComponent, onSubmit, isLoading } = useFormApi({
  api: 'auth.login', // path as found in apiRoutes
  form, // instance to ant design form
})

return (
  <Form form={form} onSubmit={onSubmit}>
    <ErrorComponent />
    <Form.Item name="username">
      <Input />
    </Form.Item>
    <Form.Item name="password">
      <Input.Password />
    </Form.Item>
    <Button type="primary" htmlType="submit" loading={isLoading}>
      Sign in
    </Button>
  </Form>
)

In this example, when the user submit the form, it'll send to the API a post request with the following fields:

{
  username: '...',
  password: '...',
}

If the API returns any error in the username or in the password, they will be displayed under the corresponding field like Ant Design would do. If it's a global error (not related to a field), it'll use the <ErrorComponent /> from the library.

You can learn more about errors handling here link

Hook options

useFormApi({
  api,
  form,
  beforeApiCall = values => values,
  afterApiCall = response => response,
  beforeErrorsHandling = errors => errors,
})

api

This parameter can be a string, an object or a function depending on the use case.

A string is used for simple routes

// apiRoutes.js
const apiRoutes = ({ post }) => ({
  auth: {
    login: post('/auth/login'),
  },
})

// useFormApiExample.js
const { onSubmit } = useFormApi({
  api: 'auth.login', // will call POST /auth/login
})

An object is used for routes with params

// apiRoutes.js
const apiRoutes = ({ patch }) => ({
  users: {
    patch: patch('/users/:name'),
  },
})

// useFormApiExample.js
const { onSubmit } = useFormApi({
  api: {
    // will call PATCH /users/janick
    path: 'users.patch',
    params: { name: 'janick' },
  },
})

A function is used when you need to resolve the path just before the request

const apiRoutes = ({ patch }) => ({
  users: {
    patch: patch('/users/:name'),
  },
})

...

const [form] = useForm()

// here form.getFieldValue('name') is evaluated at
// the creation of the hook, returning an undefined value
const {
  onSubmit,
} = useFormApi({
  api: { // no function
    path: 'users.patch', // will call PATCH /users/undefined
    params: { name: form.getFieldValue('name') }, // form.getFieldValue('name') = undefined
  },
})

// --------------------------------

const {
  onSubmit,
} = useFormApi({
  api: () => ({ // note the function here
    path: 'users.patch', // will call PATCH /users/janick if form.getFieldValue('name') = 'janick'
    params: { name: form.getFieldValue('name') },
  }),
})

form

This parameter is the form variable generated by useForm from Ant Design.

import { useForm } from 'antd'

const [form] = useForm()

const { onSubmit } = useFormApi({
  form,
})

It is used to manage fields related errors.


beforeApiCall

This parameter is used to transform the data before it is sent to the API.

const {
  onSubmit,
} = useFormApi({
  beforeApiCall: values => {
    // some processing

    return {
      ...values,
      name: 'Overriden name',
    )
  },
})

afterApiCall

This parameter is usually used to add an action after the API call is successful.

const router = useRouter()

const { onSubmit } = useFormApi({
  afterApiCall: (response) => {
    // redirects to user's profile after editing
    router.push(`/users/${response.id}`)
  },
})

beforeErrorsHandling

This parameter is called just before dispatching the errors between the form and the <ErrorComponent />, this is the best time to do any needed transformation.

const { onSubmit } = useFormApi({
  beforeErrorsHandling: (errors) => {
    // do something with errors
  },
})
0.1.5

7 months ago

0.1.4

1 year ago

0.1.3

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago