Skip to content
This repository has been archived by the owner on Feb 14, 2024. It is now read-only.

next-dev-team/nextjs-next-boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Author Generate License
Sila Rim
This file was generated to edit in src/docs/xxx.md then run command yarn gen:docs
MIT

NextJs Next Boilerplate

This is a really simple project that shows the usage of Next.js with TypeScript.

Preview Demo

Preview the demo live on nextjs-next-boilerplate.netlify.app

Online development or testing Gitpod

State Management

  • Using this custom store base on hookstate with custom plugin that we can access without hook and with hook

  • Feature

    • Easy to Use with no need to learn it's almost the same javascript reactive
    • Proxy base prevent unnecessary re-render without using selector
    • Access both hook and out side hook
    • Build-in persist like redux-persist with whitelist or blacklist
    • No need provider call it every where from store to store, store to function or hook
    • Out of box with many plugin
    • Learn more about https://hookstate.js.org/ or sample code

Create Store

import { createStore, useGlobalStore } from '@/hooks';

/**
 * this is sample of usage hookstate with persist plugin
 * @link https://hookstate.js.org/docs/global-state
 */

/**
 * init interface
 */
type IInitStore = {
  colorScheme: 'dark' | 'light';
};

//init store value with interface it will use own value as interface
const initWithOwnType = {
  counter: 0,
};

// init store value with interface it will use IInitStore for typing
const initWithType: IInitStore = {
  colorScheme: 'light',
};

// combine init type & own type together
const initStore = {
  ...initWithOwnType,
  ...initWithType,
};

// get combine type for store interface
type IStore = IInitStore & typeof initStore;

// get combine store key interface for whitelist typing
type IStoreKey = keyof IStore;

// create hook store
const store = createStore(initStore);

// store key and whitelist persist
const wrapStore = wrapGlobalStore<IStoreKey, IStore>({
  key: 'useSampleStore',
  store,
  whitelist: ['counter'],
});

/**
 *  this one is normal custom hook we can return other state too
 *  but make sure following the rule of custom hook
 */

export default function useSampleStore() {
  /**
   * useGlobalStore is a custom hook base on hookstate
   * provide persistence with whitelist or blacklist
   */
  const { state } = useGlobalStore(wrapStore);

  // usage of update state with arg
  const setCounterDecBy = (by = 1) => {
    state.counter.set((p) => p - by);
  };

  return {
    /**
     * usage of get state it will render only state.counter change due to it use proxy base
     * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
     */
    get counter() {
      return state.counter.get();
    },
    //return func as normal custom hook
    setCounterDecBy,
  } as const;
}

//return store type
export type IDemoStore = Partial<ReturnType<typeof useSampleStore>>;

//return store type and store for using outside react
export const demoStore = getGlobalStore(wrapStore);

Use store in component

import useSampleStore, { demoStore } from '@/store';

// want to access store without using hook and can use every where
const getCounter = demoStore.counter.get();
const setCounter =()=> demoStore.counter.set(10);

export default function HomePage() {
  const { counter } = useSampleStore();

  return <>
     /**
   * this counter get from store also it's persist in local storage
   * it will re-render only counter update
   */
  <h1>{counter}</h1>

// access counter without using hook
  <h1>{getCounter}</h1>

  {/*  setCounterDecBy with arg will update counter cause component re-render */}
  <button onClick={()=>setCounterDecBy(1)> </button>

  {/*  setCounter without using hook */}
  <button onClick={setCounter}> </button>

  </>;
}

GraphQl

Step 1: Generate graph QL from xx.gql file

  • create .gql file with graph ql schema

    create file xxName.gql in page/template or some where inside src for individual if want to global share should be create in graphQl/gql/xxxName.gql

ex: crate graphQl/gql/sample.gql

query post($id: ID!) {
  post(id: $id) {
    id
    title
    body
  }
}

Run command to generate there are method

  • Method1: generate without watch file so we need to generate every create new file or update xxxName.gql
yarn codegen
  • Method2: generate with watch file so it will generate every create new file or change xxxName.gql it's good for pc or computer has big RAM.
yarn codegen:watch

Generate output

you will get 3 file there are

  • hooks.tsx it will generate apollo hook to this file.
  • operations.tsx this file will generate as type base on operation of schema in xxName.gql.
  • schemas.tsx this file will generate as the whole type from graph ql api.

Usage in Apollo Client in component

  • The real sample code of using it in store/useDemoStore.ts

  • before using it don't forget to wrap withApollo for every page or global in page/app.tsx for this boilerplate already set up as global now it ready to use.

/**
 * with GraphQl useQuery
 */
export const HomePage = () => {
  const {
    data: postData,
    loading: loadingPostData,
    refetch: refetchPostData,
  } = usePostQuery({
    variables: {id: 1 }
  });

  return <h1>{postData.title}<h1/>
};

Locale

Usage of locales or i18n with i18nNext

Step 1: add locale key to language file

  • Add locale key to all language file inside src/locales/resources/xx.json

  • Translate for english

// resources/en.json
{
  "welcome": "Welcome to Nextjs Next Boilerplate",
  "settings": {
    "currentLanguage": "Current Language : {{locale}}"
  }
}
  • Translate for khmer
// resources/km.json

{
  "welcome": "សូមស្វាគមន៍មកកាន់ Nextjs Next Boilerplate",
  "settings": {
    "currentLanguage": "ភាសាបច្ចុប្បន្ន : {{locale}}"
  }
}

Step 2: Get Typescript update

  • For current issue with typescript not update new key that we have added so we need to reload IDE to get new typescript update.

  • For VS-CODE IDE

    • Mac User
command key + p then input > then input reload then click Reload Window

Step 3: Usage

export const HomePage = () => {
// get t func from store if those page already call seSettingsStore
const { t } = useSettingsStore();

//if not yet call seSettingsStore we also can call useTranslation from i18n it self
const { t } = useTranslation();

  return <>
// with dynamic text translate
   <h1> {t('settings.currentLanguage', { locale: 'khmer' })} <h1/>

// with normal translate
   <h1> {t('welcome')} <h1/>
  </>;
};

Typescript tutorial Common use in ReactJS

/**
 * interface: defined interface
 */
export interface IUser {
  name: string;
  id: number;
  gender: 'MALE' | 'FEMALE';
}

/**
 * type is like is new shorthand syntax for interface and it only a bit feature different
 */
type UserType = {
  name: string;
  id?: number;
  gender: 'MALE' | 'FEMALE';
};

type AdditionalUserType = {
  school: string;
  grade: string;
};

// usage:
export const userInfo: IUser = {
  gender: 'FEMALE',
  id: 1,
  name: 'Sila',
};

const getUserInfoFunc = () => userInfo;

/**
 * Access to object property type
 *
 * -Ex: if we want to create UserName type we don't need to write type name:string again
 * just access to use existing property in base type
 */
export type UserName = IUser['name'];

/**
 * extends: inherits the properties for other type
 * - using & sign to extends type like UserType extends AdditionalUserType...
 */
export type FullUserType = UserType &
  AdditionalUserType & {
    schoolId: string;
  };

/**
 * Partial: convert all type to optional
 */

// ex: type will not required name and id
export const partialUser: Partial<UserType> = {
  gender: 'FEMALE',
};

/**
 * Required: convert all type to required
 * -Ex: all type will be required event will put optional in our base type like id in UserType
 */

export const requiredUser: Required<UserType> = {
  id: 1,
  name: 'sila',
  gender: 'MALE',
};

/**
 * Omit: remove property in type
 * - Ex: name is remove from UserType
 */
export const omitUserType: Omit<UserType, 'name'> = {
  gender: 'MALE',
  id: 1,
};

/**
 * Pick: select property in type
 * - Ex: name is remove from UserType
 */
export const pickUserType: Omit<UserType, 'name'> = {
  gender: 'MALE',
  id: 1,
};

/**
 * Exclude: remove property in type
 * - Ex: banana is remove from FruitType
 */
type FruitType = 'coffee' | 'banana';
// excludeFruit can't equal to 'banana'
export const excludeFruit: Exclude<FruitType, 'banana'> = 'coffee';

/**
 * ReturnType get inherited type from func
 *
 * -Ex: returnTypeFunc func bellow will return UserInfoType
 */
export const returnTypeFunc = (fruit: ReturnType<typeof getUserInfoFunc>) => {
  return fruit;
};

/**
 * Generic: can be pass new type to type
 */

// U if optional because already assign sting
type dynamicTypeArg<T, U = string> = {
  name: U;
  otherInfo: Partial<T>;
};

export const dynamicData: dynamicTypeArg<UserType> = {
  name: 'Sila',
  otherInfo: {
    gender: 'FEMALE',
  },
};

// usage in normal func
export const genericArr = <T extends unknown = string>(arg1?: T) => {
  return arg1;
};
// usage in arrow func
export function genericFunc<T>(arg: T) {
  return arg;
}

// conditional generic

export type GenConditionalType<T = boolean> = T extends boolean ? boolean : string;

export const renderCondition = <T extends unknown>(v: T) => v;

const getProperty = <T, K extends keyof T>(obj: T, key: K) => {
  return obj[key];
};

const userName = getProperty({ name: 'sila' });
// log(userName.name) ==> "sila"

Back2Top

Request to server

Usage of axios with graph document

import { print } from 'graphql';
import { PostDocument } from '@/graphQl/hooks';

const getPost = async (variables?: PostQueryVariables) => {
  const res = requestAxios.post('', {
    query: print(PostDocument),
    variables,
  });
  return res;
};

Use it direct in component

const Homepage = () => {
  useEffect(() => {
    getPost().then((res) => {
      console.log(res);
    });
  }, []);

  return <h1>Hello</h1>;
};

Usage of requestAxios with rest api

Generator

this boilerplate also has generator command using yo generator inside root folder generator

Available generator command

  • yarn com: starter for component with auto import to index
yarn com
  • yarn page: starter for page
yarn page
  • yarn hook: starter for hook with auto import ot index
yarn hook
  • yarn context: starter for context with auto import ot index
yarn context
  • yarn enum: starter for hook with auto import ot index
yarn enum
  • yarn constant: starter for constant with auto import ot index
yarn constant