react ts项目相关

一、axios配置

     1.utils文件夹下面的:request.ts

import axios from "axios";
import type { AxiosInstance,AxiosError, AxiosRequestConfig } from 'axios'
import {message} from 'antd';
import { Console } from "console";

// 基础URL,axios将会自动拼接在url前
let baseURL = process.env.REACT_APP_API_URL;
const getAuthToken = (token: string) => `Bearer ${token}`;

// 默认请求超时时间
const timeout = 30000;

// 创建axios实例
const service:AxiosInstance = axios.create({
    timeout,
    baseURL,
    headers: {
      Accept: 'application/json',
      'X-Requested-With': 'XMLHttpRequest',
    },
    // 如需要携带cookie 该值需设为true
    // withCredentials:true
});

// 统一请求拦截 可配置自定义headers 例如 languange、token等
service.interceptors.request.use(
    (config) => {
        // 配置自定义请求头
        // let customHeaders: AxiosRequestHeaders = {
        //     language:'zh-cn',
        // };
        // config.headers = customHeaders;
        let token = localStorage.getItem('token')!
        if(!token){
          config.headers['authorization'] = getAuthToken(token);
        }
        return config
    },
    (error:AxiosError)=>{
        console.log(error);
        Promise.reject(error)
    }
)

// axios 返回格式
interface axiosTypes<T>{
    data:T;
    status:number;
    statusText:string;
}

  //核心处理代码 将返回一个promise 调用then将可获取响应的业务数据
  const requestHandler = <T>(method: 'get' | 'post' | 'put' | 'delete', url: string, params: object = {}, config: AxiosRequestConfig = {}): Promise<T> => {
    let response: Promise<axiosTypes<any>>;
    switch(method){
      case 'get':
        response = service.get(url, {params: { ...params }, ...config});
        break;
      case 'post':
        response = service.post(url, {...params}, {...config});
        break;
      case 'put':
        response = service.put(url, {...params}, {...config});
        break;
      case 'delete':
        response = service.delete(url, {params: { ...params }, ...config});
        break;
    }
    
    return new Promise<T>((resolve, reject) => {
      response.then(res => {
        //业务代码 可根据需求自行处理
  
        const data = res.data;
        console.log('res:',res)
        if(data.errCode !== 0){
          
          //特定状态码 处理特定的需求
          // if(data.errCode == 401){
          //   message.warning('您的账号已登出或超时,即将登出...');
          //   console.log('登录异常,执行登出...');
          // }
  
          // let e = JSON.stringify(data);
          // message.warning(`请求错误:${data}`);
          console.log(`请求错误:${data}`)
          //数据请求错误 使用reject将错误返回
          reject(data);
        }else{
          //数据请求正确 使用resolve将结果返回
          resolve(data);
        }
  
      }).catch(error => {
        // let e = JSON.stringify(error);
        message.warning(`网络错误:${error}`);
        // console.log(`网络错误:${e}`)
        console.log(`网络错误:${error}`)
        reject(error);
      })
    })
  }
  
  // 使用 request 统一调用,包括封装的get、post、put、delete等方法
  const request = {
    get: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('get', url, params, config),
    post: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('post', url, params, config),
    put: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('put', url, params, config),
    delete: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('delete', url, params, config)
  };
  
  // 导出至外层,方便统一使用
  export { request };

2.api文件夹下面的list.ts接口封装例子:

import { request } from "../utils/request";
import { baseUrls } from "../utils/baseUrlConfig";


//登录
export const LoginApi = (params: any) => request.post<any>(`${baseUrls.baseUrl1}/auth/login`, params, {timeout: 15000});

export const getArticleList =(params: any) => request.post<any>(`${baseUrls.baseUrl1}/cms_admin/get_article_list`, params, {timeout: 15000});

3.将api文件夹下面封装的接口导出,index.ts

import * as list from './list';

const api = {
    list,
};

export default api;

4.组件中对接口的使用

import {FC,useState} from 'react';
import { useNavigate } from 'react-router-dom';
import api from '@/api';
import md5 from "md5";

export interface LoginProps{}

export const Login:FC<LoginProps> = ()=>{
    const navigate = useNavigate();
    const [useName,setUseName] = useState<string>('');
    const [password,setPassword] = useState<string>('');
    const Login = ()=>{
        api.list.LoginApi({
            mobile:useName,
            password:md5(password),
            platform:5,
            operationID:Date.now()+''
        }).then(res=>{
            console.log(res)
            const token = res?.data?.token
            localStorage.setItem('token',token)
            navigate('/')
        }).catch(err=>{
            console.log(err)
        })
    }
    return (
        <div style={{width:'500px',height:'500px',border:'1px solid red',margin: '20px auto'}}>
            <div style={{width:'300px',margin: '20px auto'}}>
                <span style={{ height:'30px', width:'100px',}}>用户名:</span>
                <input type="text" style={{ height:'30px', width:'200px',}} value={useName} onChange={e=>setUseName(e.target.value)}/>
            </div>
            <div style={{width:'300px',margin: '20px auto'}}>
                <span style={{ height:'30px', width:'100px',}}>密码:</span>
                <input type="password" style={{ height:'30px', width:'200px',}} value={password} onChange={e=>setPassword(e.target.value)}/>
            </div>
            <div style={{width:'200px',margin: '20px auto'}}><button style={{ height:'30px', width:'200px',}} onClick={Login}>登录</button></div>
        </div>
    )
}

二、redux配置

1.store文件夹下面的homeReducer.ts,userReducer.ts片

//homeReducer.ts
import { createSlice,PayloadAction } from "@reduxjs/toolkit";

// import { RootState } from "../Store";

interface HomeState{
    count:number;
}

const initialState:HomeState={
    count:0,
}

export const HomeReducer = createSlice({
    name:'Home',
    initialState,
    reducers:{
        addCount:state=>{
            state.count+=1
        },
        changeCount:(state,action:PayloadAction<number>)=>{
            state.count = state.count*action.payload
        },
        reduceCount:state=>{
            state.count-=1;
        }
    }
})

export const {addCount,changeCount,reduceCount} = HomeReducer.actions;

export default HomeReducer.reducer;


//userReducer.ts

import { createSlice,PayloadAction } from "@reduxjs/toolkit";

// import { RootState } from "../Store";

interface UserState{
    count:number;
}

const initialState:UserState={
    count:0,
}

export const userReducer = createSlice({
    name:'user',
    initialState,
    reducers:{
        addCount:state=>{
            state.count+=1
        },
        changeCount:(state,action:PayloadAction<number>)=>{
            state.count = state.count*action.payload
        },
        reduceCount:state=>{
            state.count-=1;
        }
    }
})

export const {addCount,changeCount,reduceCount} = userReducer.actions;

export default userReducer.reducer;

2.store文件夹下面的store.ts

import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userReducer/userReducer';
import homeReducer from './homeReducer/homeReducer';

const store = configureStore({
  reducer: {
    user: userReducer,
    home:homeReducer,
  },
})

export default store;

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

3.使用例子

//APP.tsx
function App() {
  return (
   <Provider store={store}>
     <Router>
      <Suspense>
        <TestAppRouter/>
      </Suspense>
    </Router>
   </Provider>
  );
}

//组件中使用
import { useSelector,useDispatch } from "react-redux";
import { RootState } from "@/store/Store";
import {addCount,changeCount} from '@/store/userReducer/userReducer';

 const dispatch = useDispatch();
  const count = useSelector((state:RootState)=>state.user.count);
dispatch(addCount());dispatch(changeCount(5))

三、react-router-dom的配置

1.Router文件夹下面的TestAppRouter.ts

import {
    Route,
    Routes,
    RoutesProps
  } from "react-router-dom";
  import {FC,lazy} from 'react';

  const Home = lazy(()=>import('@/pages/Home'));
  const Login = lazy(()=>import('@/pages/Login'));
  const Regist = lazy(()=>import('@/pages/Regist'));
  const Editor = lazy(()=>import('@/components/Editor'));
  const NewEditor = lazy(()=>import('@/components/NewEditor'));

  export interface TalkRouterProps extends RoutesProps {}

  export const TestAppRouter:FC<TalkRouterProps> = (props)=>{
    return (
        <Routes {...props}>
          <Route path="/" element={<Home/>}>
            <Route path='editor' element={<Editor/>}/>
            <Route path ='neweditor' element={<NewEditor/>}/>
          </Route>
          <Route path='/login' element={<Login/>} />
          <Route path ='/register' element={<Regist/>} />
        </Routes>
    )
  }

2.在APP.tsx中的使用

import React,{Suspense} from 'react';
import './App.css';
import { BrowserRouter as Router } from "react-router-dom";
import {TestAppRouter} from '@/Router/TestAppRouter';
import { Provider } from 'react-redux';
import store from './store/Store';

function App() {
  return (
   <Provider store={store}>
     <Router>
      <Suspense>
        <TestAppRouter/>
      </Suspense>
    </Router>
   </Provider>
  );
}

export default App;

3.Outlet的使用,home.tsx

import { FC, useEffect } from "react";

import { useNavigate, Outlet } from "react-router-dom";
import { useIsPath } from "@/Func/Func";
import { useSelector,useDispatch } from "react-redux";
import { RootState } from "@/store/Store";
import {addCount,changeCount} from '@/store/userReducer/userReducer';
import api from "@/api";


export interface HomeProps {}


export const Home: FC<HomeProps> = () => {
  const navigate = useNavigate();
  const [, isPath] = useIsPath("/editor");
  const [, isPath2] = useIsPath("/neweditor");
  const dispatch = useDispatch();
  const count = useSelector((state:RootState)=>state.user.count);
  const getarticle = ()=>{
    api.list.getArticleList({
      areaCode:'CA',
      operationID:"219396618",
      pageNumber:1,
      showNumber:10000,
      status:0
    }).then(res=>{
      console.log(res)
    }).catch(err=>{
      console.log(err)
    })
  }
  useEffect(() => {
    console.log(isPath);
  }, [isPath]);
  return (
    <div>
      <div className="editor">
        <div style={{ margin: "10px" }}>Home</div>
        <div style={{ margin: "10px" }}>
          <button
            onClick={() => (!isPath ? navigate("/editor") : navigate("/"))}
          >
            {!isPath ? "go ediitor" : "go home"}
          </button>
          <button
            onClick={() => (!isPath2 ? navigate("/neweditor") : navigate("/"))}
          >
            {!isPath2 ? "go newediitor" : "go home"}
          </button>
          <span style={{marginLeft:'10px',marginRight:'10px'}}>{count}</span>
          <button onClick={()=>dispatch(addCount())}>add</button>
          <button onClick={()=>dispatch(changeCount(5))}>change</button>
          <button onClick={()=>getarticle()}>获取</button>
          <button onClick={()=>navigate('/login')}>去登录页</button>
        </div>
      </div>
      <div>
        <Outlet />
      </div>
    </div>
  );
};

四、@配置

1.yarn add  @craco/craco 或者 npm install @craco/craco --save
2.修改 package.json 文件(改变启动)
"scripts": {
  //"start": "react-scripts start",
  // "build": "react-scripts build",
  //"test": "react-scripts test",
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
}
3.在根目录下创建craco.config.js文件(用于修改默认配置)
const path = require('path')

module.exports = {
  // webpack 配置
  webpack: {
    // 配置别名
    alias: {
      // 约定:使用 @ 表示 src 文件所在路径
      '@': path.resolve(__dirname, 'src'),
    },
  },
}
4.在tsconfig.json中配置
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": "src",
    "paths": {
      "@/*": ["*"]
    }
  },
  "include": ["src", "craco.config.js"]
}

五、扩展redux-persist(状态永久存储,防刷新数据消失)

<1>安装:npm i @reduxjs/toolkit react-redux redux-persist;

<2>store.ts代码

import { configureStore, combineReducers } from "@reduxjs/toolkit";
import ListSlice from "./reducer/ListSlice";
import GoodSlice from "./reducer/GoodSlice";

import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from "redux-persist";
import storage from "redux-persist/lib/storage";

//所有的的状态片
const reducer = combineReducers({
  ListSlice,
  GoodSlice,
});
const persistConfig = {
  key: "redux",
  storage: storage,
  whitelist: ["ListSlice"], //白名单--需要永久存储的片数据,其他的片段则不永久存储页面刷新则数据消失
  // blacklist:['CollapsedSlice'],//黑名单仅不保存CollapsedSlice
};

const persistedRedcer = persistReducer(persistConfig, reducer);

const store = configureStore({
  reducer: persistedRedcer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        //忽略了 Redux Persist 调度的所有操作类型。这样做是为了在浏览器控制台读取a non-serializable value was detected in the state时不会出现错误。
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
});

export const persistor = persistStore(store);

export default store;
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

<3>ListSlice.ts代码

import { createSlice ,PayloadAction} from "@reduxjs/toolkit";
type initialStateType={
    price:number
}
const initialState:initialStateType = {
    price:15
}

export const ListSlice = createSlice({
    name:'ListSlice',
    initialState:initialState,
    reducers:{
        addPrice:(state,action:PayloadAction<number>)=>{state.price=state.price+action.payload},
        changePrice:(state,action:PayloadAction<number>)=>{state.price=state.price-action.payload}
    }
})

// 导出actions
export const {addPrice,changePrice} = ListSlice.actions;

// 导出reducer,在创建store中使用
export default ListSlice.reducer;

<4>GoodSlice.ts代码

import {createSlice,PayloadAction} from '@reduxjs/toolkit';
type initialStateType={
    name:string
}

const initialState:initialStateType={
    name:'小明'
}

export const GoodSlice = createSlice({
    name:'GoodSlice',
    initialState,
    reducers:{
        changeName:(state,action:PayloadAction<string>)=>{state.name=action.payload}
    }
})
// 导出actions
export const {changeName} = GoodSlice.actions;

// 导出reducer
export default GoodSlice.reducer

<5>如何使用:

import { changeName } from "@/store/reducer/GoodSlice";
import { addPrice, changePrice } from "@/store/reducer/ListSlice";
import { FC,useRef,useState,useEffect } from "react";
import { useDispatch,useSelector } from "react-redux";
import { RootState } from "@/store/store";

// import { CSSTransition  } from 'react-transition-group';

interface LoginProps {}

export const Login: FC<LoginProps> = () => {
  const dispatch = useDispatch();
  const price = useSelector((state:RootState)=>state.ListSlice.price);
  const name = useSelector((state:RootState)=>state.GoodSlice.name);
    const [flag,setFlag] = useState<boolean>(true);
    const ref = useRef<HTMLDivElement>(null);
    const ToCheck = ()=>{setFlag(flag?false:true);dispatch(addPrice(2))};
    useEffect(()=>{
       if(!flag){
        ref.current&&ref.current.setAttribute('class','switchs-change2');
       }else{
        ref.current&&ref.current.setAttribute('class','switchs-change')
       }
    },[flag])
    useEffect(()=>{
      console.log(JSON.parse(localStorage.getItem('persist:redux')!))
    },[])
  return (
    <div>
      <div onClick={()=>dispatch(changePrice(1))}>登录页面---={price}===={name}</div>
      <button onClick={()=>dispatch(changeName('小李'))}>修改name</button>
      <div className="switchs" onClick={ToCheck}>
        <div className="switchs-change" ref={ref}>
          <div className="switchs-on">ON</div>
          <div className="switchs-nbsp">&nbsp;</div>
          <div className="switchs-off">OFF</div>
        </div>
      </div>
    </div>
  );
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值