Reagieren Sie auf das REST-API-Framework + TypeScript + Styled-Components

Guter% time_day, Khabrovites!





Ich habe noch keine Artikel geschrieben, es gibt ΓΌberhaupt keine Erfahrung mit dem Wort. Bitte urteilen Sie daher nicht streng und ich entschuldige mich im Voraus, wenn ich irgendwo Fehler mache.





Dies ist eine vΓΆllig neue Erfahrung fΓΌr mich. Ich hatte noch nie die Gelegenheit, meine Erfahrungen und Best Practices mit der Community zu teilen.





Vorwort

React, , - , , , .



- , , , - "", "" .., , , , , , , , .



, , . , "Core". , , , , - , ( , ), .





(!) , , , , - - , . , ( , React), .



, , .





Components

, , .





, , :





  • (Smart)





  • (Ordinary)





  • (Simple)





  • UI (UI, )





  • (Containers)





  • (Pages)





(Smart, Ordinary, Simple UI) Components.





:





  • UI - , () : button, input, textarea, select .





    • .





  • Simple - , , , - , - .





    • .





    • , , React ( useState).





    • UI .





  • Ordinary - , - , -.





    • , .





    • , , React ( useState).





    • Simple UI .





  • Smart - , , -





    • , ( )





    • , ,





    • Ordinary, Simple UI .





Componets:





.
└── src/
    β”œβ”€β”€ components/
    β”‚   β”œβ”€β”€ ordinary
    β”‚   β”œβ”€β”€ simple
    β”‚   β”œβ”€β”€ smart
    β”‚   └── ui
    └── ...
      
      



(Containers Pages) ( src).





  • Containers - , , , , , , , , - , , .





  • Pages - , Components, . , , .





:





.
└── src/
    β”œβ”€β”€ components/
    β”‚   β”œβ”€β”€ ordinary
    β”‚   β”œβ”€β”€ simple
    β”‚   β”œβ”€β”€ smart
    β”‚   └── ui
    β”œβ”€β”€ containers
    β”œβ”€β”€ pages
    └── ...
      
      



, 2 ( ) :





  • index.tsx - ,





  • styled.ts - , ( styles.sss, styles.css, , )





Align. , "Simple", ( ) , , UI .





// index.tsx

import React, { memo } from "react";
import * as S from "./styled"; //   

const Align = memo(({ children, axis, isAdaptable = false }: Readonly<Props>) => {
	return (
		<S.Align $axis={axis} $isAdaptable={isAdaptable}>
			{children}
		</S.Align>
	);
});

export { Align };
export interface Props {
	axis: S.Axis;
	children?: React.ReactNode;
	isAdaptable?: boolean;
}
      
      



// styled.ts

import styled, { css } from "styled-components";

const notAdaptableMixin = css`
	width: 100%;
	height: 100%;
	max-height: 100%;
	max-width: 100%;
`;

const adaptableMixin = css<AlignProps>`
	width: ${(props) => !props.$axis.includes("x") && "100%"};
	height: ${(props) => !props.$axis.includes("y") && "100%"};
	min-width: ${(props) => props.$axis.includes("x") && "100%"};
	min-height: ${(props) => props.$axis.includes("y") && "100%"};
`;

export const Align = styled.div<AlignProps>`
	display: flex;
	flex-grow: 1;
	justify-content: ${(props) => (props.$axis.includes("x") ? "center" : "start")};
	align-items: ${(props) => (props.$axis.includes("y") ? "center" : "start")};
	${(props) => (props.$isAdaptable ? adaptableMixin : notAdaptableMixin)};
`;

export interface AlignProps {
	$axis: Axis;
	$isAdaptable?: boolean;
}

export type Axis = ("y" | "x")[] | "x" | "y";

      
      



, ...





Core

"" . , , , ..





:





  • Config - ( , )





  • Constants - , ( )





  • Hooks - (, ).





  • Models - , .





  • Schemes - , ..





  • Services - , .





  • Store - ( MobX), Redux, , ..





  • Theme ( Styled-Components) - .





  • Types - , .





  • Utils - , , , , .





  • api.ts - HTTP ( axios), - ( - , ).





// config/api.config.ts

export const serverURI = "http://localhost:8080";
export const routesPrefix = '/api/v1';


// config/routes.config.ts

import { routesPrefix } from "./api.config";

export const productBrowserRoutes = {
	getOne: (to: string = ":code") => `/product/${to}`,
	search: (param: string = ":search") => `/search/${param}`,
};

export const productAPIRoutes = {
	getOne: (code: string) => `${routesPrefix}/product/code/${code}`,
	search: () => `${routesPrefix}/product/search`,
};
      
      



- . , , , , .





// constants/message.constants.ts

export const UNKNOWN_ERROR = " ";
      
      



// hooks/useAPI.ts
//     
/* eslint-disable react-hooks/exhaustive-deps */

import { useCallback, useEffect } from "react";
import { useLocalObservable } from "mobx-react-lite";
import type { API, Schema, Take } from "@core/types";

function useAPI<
	F extends API.Service.Function<API.Response<any>>,
	R extends Take.FromServiceFunction.Response<F>,
	P extends Parameters<F>
>(service: F, { isPendingAfterMount = false, isIgnoreHTTPErrors = false }: Options = {}) {
	const localStore = useLocalObservable<Store>(() => ({
		isPending: {
			value: isPendingAfterMount,
			set: function (value) {
				this.value = value;
			},
		},
	}));

	const call = useCallback(
		async (...params: P): Promise<R["result"]> => {
			localStore.isPending.set(true);

			try {
				const { data } = await service(...params);
				const { result } = data;

				localStore.isPending.set(false);

				return result;
			} catch (error) {
				if (isIgnoreHTTPErrors === false) {
					console.error(error);
				}

				localStore.isPending.set(false);

				throw error;
			}
		},
		[service, isIgnoreHTTPErrors]
	);

	const isPending = useCallback(() => {
		return localStore.isPending.value;
	}, []);

	useEffect(() => {
		localStore.isPending.set(isPendingAfterMount);
	}, [isPendingAfterMount]);

	return {
		call,
		isPending,
	};
}

export { useAPI };
export interface Options {
	isPendingAfterMount?: boolean;
	isIgnoreHTTPErrors?: boolean;
}

type Store = Schema.Store<{ isPending: boolean }>;
      
      



// models/product.model.ts
//   

export interface ProductModel {
	id: number;
	name: string;
	code: string;
	info: {
		description: string;
		note: string;
	};
	config: {
		isAllowedForPurchaseIfInStockZero: boolean;
		isInStock: boolean;
	};
	seo: {
		title: string;
		keywords: string;
		description: string;
	};
}
      
      



// services/product.service.ts
//     

import { api } from "../api";
import { routesConfig } from "../config";
import type { ProductModel } from "../models";
import type { API } from "../types";

export function getOne(code: string) {
	return api.get<API.Service.Response.GetOne<ProductModel>>(
		routesConfig.productAPIRoutes.getOne(code)
	);
}
      
      



// theme/index.ts
//  

import { DefaultTheme } from "styled-components";

export const theme: DefaultTheme = {
	colors: {
			primary: "#2648f1",
			intense: "#151e27",
			green: "#53d769",
			grey: "#626b73",
			red: "#f73d34",
			orange: "#fdb549",
			yellow: "#ffe243",
			white: "white",
		},
};
      
      



// types/index.tsx
//  

import type { AxiosResponse } from "axios";

export namespace API {
	export namespace Service {
		export namespace Response {
			export type Upsert<T> = Response<T | null>;
			export type GetOne<T> = Response<T | null>;
			export type GetMany<T> = Response<{
				rows: T[];
				totalRowCount: number;
				totalPageCount: number;
			}>;
		}
		export type Function<T extends API.Response<any>, U extends any[] = any[]> = (
			...params: U
		) => Promise<AxiosResponse<T>>;
	}
	export type Response<T> = {
		status: number;
		result: T;
	};
}
      
      



// utils/throttle.ts

function throttle<P extends any[]>(func: (...params: P) => any, limit: number) {
	let inThrottle: boolean;

	return function (...params: P): any {
		if (!inThrottle) {
			inThrottle = true;
			func(...params);
			setTimeout(() => (inThrottle = false), limit);
		}
	};
}

export { throttle };
      
      



// store/index.tsx

import { createContext } from "react";
import { useLocalObservable } from "mobx-react-lite";

import { app, App } from "./segments/app";
import { layout, Layout } from "./segments/layout";
import { counters, Counters } from "./segments/counters";

export const combinedStore = { layout, app, counters };
export const storeContext = createContext<StoreContext>(combinedStore);
export function StoreProvider({ children }: { children: React.ReactNode }) {
	const store = useLocalObservable(() => combinedStore);

	return <storeContext.Provider value={store}>{children}</storeContext.Provider>;
}

export type StoreContext = {
	app: App;
	layout: Layout;
	counters: Counters;
};
      
      



// api.ts
//  AXIOS    

import axios from "axios";
import { apiConfig } from "./config";

const api = axios.create({
	baseURL: apiConfig.serverURI,
});

api.interceptors.request.use((req) => {
	return {
		...req,
		baseURL: apiConfig.serverURI,
	};
});

export { api };
      
      



! .





...

, , :





  • Assets - , : , , .. (, , )





  • Routes - ( , ) ( ).





  • Styles - , , .





// routes/index.tsx

import { Switch, Route } from "react-router-dom";

//  
import { Product } from "../pages/Product";
...
import { NotFound } from "../pages/NotFound";

import { routesConfig } from "../core/config";

const Routes = () => {
	return (
		<Switch>
			<Route exact path={routesConfig.productBrowserRoutes.getOne()}>
				<Product />
			</Route>
			{/*  -  */}
			<Route>
				<NotFound />
			</Route>
		</Switch>
	);
};

export { Routes };

      
      



2 :





  • app.tsx -





:





// app.tsx

import React, { useEffect } from "react";

//  
import { Routes } from "./routes";

const App = () => {
	return (
			<Routes />
	);
};

export { App };

      
      



  • index.tsx -





:





import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { ThemeProvider } from "styled-components";

//  
import { App } from "./app";
//   
import { BodyStyles } from "./styles";

import { StoreProvider } from "../core/store";
//  
import { theme } from "../core/theme";

import reportWebVitals from "./reportWebVitals";

const app = document.getElementById("app");

ReactDOM.render(
	<React.StrictMode>
		<ThemeProvider theme={theme}>
			<BodyStyles />
			<BrowserRouter>
				<StoreProvider>
					<App />
				</StoreProvider>
			</BrowserRouter>
		</ThemeProvider>
	</React.StrictMode>,
	app
);

reportWebVitals();

      
      



, , .





:





.
└── src/
    β”œβ”€β”€ assets/
    β”‚   β”œβ”€β”€ fonts
    β”‚   └── icons
    β”œβ”€β”€ components/
    β”‚   β”œβ”€β”€ ordinary
    β”‚   β”œβ”€β”€ simple
    β”‚   β”œβ”€β”€ smart
    β”‚   └── ui
    β”œβ”€β”€ containers
    β”œβ”€β”€ core/
    β”‚   β”œβ”€β”€ config
    β”‚   β”œβ”€β”€ constants
    β”‚   β”œβ”€β”€ hooks
    β”‚   β”œβ”€β”€ models
    β”‚   β”œβ”€β”€ schemes
    β”‚   β”œβ”€β”€ services
    β”‚   β”œβ”€β”€ store
    β”‚   β”œβ”€β”€ theme
    β”‚   β”œβ”€β”€ types
    β”‚   β”œβ”€β”€ utils
    β”‚   └── api.ts
    β”œβ”€β”€ pages
    β”œβ”€β”€ routes
    β”œβ”€β”€ styles
    β”œβ”€β”€ app.tsx
    └── index.tsx
      
      



- , .





( , ).





.








All Articles