npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sinoui/use-rest-list-api

v1.0.0

Published

[![npm version](https://img.shields.io/npm/v/@sinoui/use-rest-list-api)](https://www.npmjs.com/package/@sinoui/use-rest-list-api) [![downloads](https://img.shields.io/npm/dm/@sinoui/use-rest-list-api)](https://www.npmjs.com/package/@sinoui/use-rest-list-a

Downloads

3

Readme

use-rest-list-api

npm version downloads

不分页列表与 RESTful CRUD API 交互的状态管理

它可以帮助我们:

  • 管理列表的查询
  • 与 RESTful CRUD API 交互
  • 列表数据维护
  • 查询条件与浏览器的 url 同步

目录:

安装

yarn add @sinoui/use-rest-list-api

或者

npm i --save @sinoui/use-rest-list-api

快速使用

import React from 'react';
import useRestListApi from '@sinoui/use-rest-list-api';

interface User {
  userId: string;
  userName: string;
}

function ListDemo() {
  const dataSource = useRestListApi<User>('/apis/users');

  return (
    <div>
      {dataSource.isLoading && <div>正在加载人员列表数据...</div>}
      <h1>人员列表</h1>
      {dataSource.items.map((user) => (
        <div key={user.userId}>{user.userName}</div>
      ))}
    </div>
  );
}

RESTful CRUD API

假定我们要维护一组人员数据,获取人员列表的 url 是/users

获取列表数据

请求

GET /users?sex=male&sort=firstName&sort=lastName,desc

请求参数说明:

  • sex=male - 表示列表的过滤条件。@sinoui/use-rest-list-api 默认将过滤条件放在查询字符串中。
  • sort - 排序,默认格式为propertyName[,asc|desc],如果有多个,则按照sort=propertyName&sort=propertyName2,desc这样的方式编排

注意:这是@sinoui/use-rest-list-api 默认发送分页查询请求的格式,你的 RESTful API 如果不是这样的,那么你需要定制列表查询请求

响应

后端返回 json 格式数据,数据如下:

[
  {
    id: '1',
    firstName: '张',
    lastName: '三',
    sex: 'male',
  },
  {
    id: '2',
    firstName: '李',
    lastName: '四',
    sex: 'male',
  },
];

注意:如果你的 API 响应的数据格式不是这样的,那么你可以定制列表查询响应转换器,将 API 响应数据转换成上面说的数据格式即可。

获取单个数据

有时为了展现详情数据,而列表返回的数据不是很全,这时你就需要通过 API 获取单个数据。

请求

按照 RESTful 风格设计的 API,请求如下:

GET /users/1

响应

返回 JSON 格式数据。

{
  "id": "1",
  "firstName": "张",
  "lastName": "三",
  "sex": "male",
  "birthday": "1999-01-12"
}

注意:如果你的 API 响应数据格式不一致,你可以通过定制请求单个数据响应转换器,来转换成这样的数据格式。

新增数据

请求

POST /users

请求参数: (itemInfo: T,isNeedUpdate: boolean = true, idx: number = -1)

// 要新增的数据
{
  "firstName": "王",
  "lastName": "五",
  "sex": "female",
  "birthday": "2000-08-12"
},
// 新增操作完成之后是否需要刷新页面,默认true
false,
// 指定新增数据的插入位置,默认-1,在数据的末尾添加
-1

注意:如果你的 API 请求要新增的数据格式不一致,你可以通过定制新增请求的数据转换器,将上面的数据格式转换成满足你的 API 的数据格式。

响应

返回 JSON 格式的数据:

{
  "id": "3",
  "firstName": "王",
  "lastName": "五",
  "sex": "female",
  "birthday": "2000-08-12"
}

注意:如果你的 API 响应数据格式不一致,你可以通过定制新增响应的数据转换器,将上面的数据格式转换成满足你的 API 的数据格式。

更新数据

请求

PUT /users/3

请求体是 JSON 格式数据:

{
  "id": "3",
  "firstName": "王",
  "lastName": "五",
  "sex": "male",
  "birthday": "2000-08-12"
}

注意:如果你的 API 请求数据格式不一致,你可以通过定制更新请求的数据转换器,将上面的数据格式转换成满足你的 API 的数据格式。

响应

返回 JSON 格式的数据:

{
  "id": "3",
  "firstName": "王",
  "lastName": "五",
  "sex": "male",
  "birthday": "2000-08-12"
}

注意:如果你的 API 响应数据格式不一致,你可以通过定制更新响应的数据转换器,将上面的数据格式转换成满足你的 API 的数据格式。

删除数据

请求

删除单个数据:

DELETE / users / 1

删除多条数据:

DELETE /users/1,2,3

注意:如果你的 API 不支持删除多条数据,那么请设置options.useMultiDeleteApifalse

响应

返回 200、201 等 2xx 状态码表示删除成功即可。

数据结构

排序信息

排序:

interface SortInfo {
  direction: 'desc' | 'asc';
  property: string;
}

列表查询响应

useRestListApi 默认列表查询的数据结构如下:

T[]

useRestListApi 参数说明

const dataSource = useRestListApi<T, PageData>(
    url: string,
    defaultValue?: PageData<T>,
    options?: Options
);

url

指定加载列表数据的url,一般为 RESTful CRUD API 中加载列表的url,也就是基础 url。加载列表数据的 url 与基础 url 不一致,可以通过options.baseUrl设定基础 url。

defaultValue

指定默认的列表分页数据,默认为:

T[]

options

配置:

  • baseUrl - 指定 curd api 的基础url,如果不指定,则默认为url
  • defaultSearchParams - 指定默认的查询条件。
  • defaultSort - 指定默认的排序规则。
  • syncToUrl - 如果为true,则会同步查询条件与浏览器 URL。默认为false
  • keyName - 指定唯一键属性名,默认为id
  • useMultiDeleteApi - 是否启动删除多条数据的 API。默认为true,表示启用。见删除数据章节。
  • transformListResponse - 指定分页列表查询结果的转换器。
  • transformListRequest - 指定分页查询条件转换器。
  • transformFetchOneResponse - 指定获取单条数据的响应数据转换器。
  • transformSaveRequest - 指定新增数据的请求数据转换器。
  • transformSaveResponse - 指定新增数据的响应数据转换器。
  • transformUpdateRequest - 指定更新数据的请求数据转换器。
  • transformUpdateResponse - 指定更新数据的响应数据转换器。
  • transformRemoveResponse - 指定删除数据的响应数据转换器。

转换器可以用来定制你的 API 细节。会用一个章节来介绍。

转换器

如果你的 API 数据格式与@sinoui/use-rest-list-api 默认支持的不同,那么你可以使用转换器来实现定制,让@sinoui/use-rest-list-api 为你的 API 服务。

定制列表查询请求

使用transformListRequest来定制列表查询请求。例如下面的转换器:

import qs from 'qs';

export default function transformListRequest(
  searchParams: {
    [key: string]: string;
  },
  sorts: SortInfo[],
) {
  return qs.stringify(
    {
      ...searchParams,
      sort: sorts.map(
        (sortInfo) =>
          `${sortInfo.property}${sortInfo.direction === 'desc' ? '_desc' : ''}`,
      ),
    },
    {
      arrayFormat: 'comma',
    },
  );
}

应用这个转换器后,发送的分页列表查询将会是下面的格式:

GET /users?sex=male&sort=firstName,lastName_desc

推荐使用qs来处理请求参数的序列化和解析。这里用到了arrayFormat配置,设定为comma,那么遇到数组时,则会采用","的方式将多个数据连接在一起。arrayFormat 的几个参数如下所示:

qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' });
// 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' });
// 'a[]=b&a[]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' });
// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' });
// 'a=b,c'

transformListRequest 方法结构如下:

interface SearchParams {
  [key: string]: any;
}

/**
 * 转换列表查询的请求
 *
 * @param searchParams 查询条件
 * @param sorts 排序信息
 *
 * @return {string} 返回列表查询请求的查询字符串。需要是字符串格式的。
 */
function transformListRequest(
  searchParams: SearchParams,
  sorts: SortInfo[],
): string;

定制列表查询响应转换器

使用transformListResponse来转换分页列表查询响应的数据格式。如下所示的Hacker News API转换器:

interface HackerNew {
  objectID: string;
  title: string;
  url: string;
  auth: string;
  tags: string[];
}

interface HackerNewsListResponse {
  hits: HackerNew[];
  nbHits: number;
  page: number;
  nbPages: number;
  hitsPerPage: number;
}

function transformListResponse(
  response: HackerNewsListResponse,
): PageResponse<HackerNew> {
  return response.hits,
}

transformListResponse 函数的结构如下:

function transformListPresponse<T, Response>(
  response: Response,
): PageResponse<T>;

定制请求单个数据响应转换器

使用transformFetchOneResponse定制请求单个数据响应的数据格式。例如下面的示例:

interface User {
  userId: string;
  firstName: string;
  lastName: string;
}

interface Response {
  result: User;
  status: boolean;
}

function transformFetchOneResponse(response: Response): User {
  return response.result;
}

transformFetchOneResponse 函数的结构如下:

function transformFetchOneResponse<T, Response>(respone: Response): T;

定制新增请求的数据转换器

使用transformSaveRequest定制新增数据请求转换器。例如:

interface SaveUserInfo {
  user: User;
  time: long;
}

function transformSaveRequest(user: User): SaveUserInfo {
  return {
    user,
    time: new Date().getTime(),
  };
}

transformSaveRequest 函数的数据结构如下:

function transformSaveRequest<T, NewRequestData>(
  data: T,
  headers: { [key: string]: string },
): NewRequestData;

定制新增响应的数据转换器

使用transformSaveResponse定义新增响应的数据格式转变。例如:

/**
 * 新增API响应返回数据的结构
 */
interface ResponseData {
  result: User;
  status: boolean;
  errorMessage?: string;
}

function transformSaveResponse(responseData: ResponseData): User {
  if (response.status) {
    return response.result;
  }

  throw new Error('获取数据失败');
}

transformSaveResponse 函数的数据结构如下:

function transformSaveResponse<T, Response>(response: Response): T;

定制更新请求的数据转换器

使用transformUpdateRequest定制更新请求。用法与transformSaveRequest一致。

定制更新响应的数据转换器

使用transformUpdateResponse定制更新请求。用法与transformSaveResponse一致。

定制删除响应的数据转换器

/** 删除API的响应数据结构 */
interface ResponseData {
    code:string;
    msg:string;
}

function transformRemoveResponse(response:ResponseData):void {
    if(code==='200'){
        alert('删除成功');
    } else {
        alert('删除失败');
        throw new Error(response.msg);
    }
}

dataSource 的属性和方法

const dataSource = useRestListApi<User, ListRawResponse>('/users');

我们的组件可以通过dataSource与查询结果、查询条件、RESTful API 进行沟通。

获取查询数据

 // 获取当前页列表数据
const users: User[] = dataSource.items;

// 获取id为'1'的用户数据
const user: User = dataSource.getItemById('1');

// 更新id为'1'的用户数据
const newUser = {...user, 'sex': 'female'};
dataSource.updateItem(newUser);

// 更新部分字段
dataSource.setItem('1', 'sex', 'female');
dataSource.setItem('1', { birthday: '2000-10-12' });

//替换items
dataSource.setItems([{id:'1',birthday:'2019-01-01'},{id:'2',age:32}])

// 新增
dataSource.addItem({id: '5', firstName: '赵', lastName: '六'}, false, -1);

// 删除id为'1'的用户数据
dataSource.removeItemById('3');

// 删除多条数据
dataSource.removeItemsByIds(['1', '2', '3']);

// 设置默认查询条件
dataSource.setDefaultSearchParams({userName:'张三'});

// 删除指定行的数据,从0开始
dataSource.removeItemAt(5)

// 获取原始响应数据
const rawResponse = dataSource.rawResponse;

// 获取是否正在加载列表数据的状态
const isLoading = dataSource.isLoading;

// 获取是否加载列表数据失败的状态
const isError = dataSource.isError;

注意:这里介绍的getItemByIdupdateItemsetItemaddItemremoveItemById这些方法只会与dataSource.items进行交互,不会与 RESTful CRUD API 进行交互。如果需要与 RESTful CRUD API 交互,参见与增删改查 API 交互

排序

// 按照姓氏倒序排序
dataSource.sortWith([
  {
    property: 'firstName',
    direction: 'desc',
  },
  {
    property: 'lastName',
    direction: 'asc',
  },
]);

列表查询

// 根据查询条件获取数据
dataSource.query(searchParams);

// 获取查询条件
dataSource.searchParams;
// 获取默认的查询条件
dataSource.defaultSearchParams;

// 重新获取当前页的数据
dataSource.reload();

fetch()方法是查询列表的基础方法,它的语法格式如下:

function fetch<T>(
  searchParams?: SearchParams,
  sorts?: SortInfo[],
): PageResponse<T>;

与增删改查 API 交互

// 获取id为'1'的数据
const user = await dataSource.get('1');

// 新增用户数据
const user = await dataSource.save(
  { firstName: '张', lastName: '三' },
  false,
  -1,
);

// 修改用户数据
const user = await dataSource.update({
  id: '1',
  firstName: '张',
  lastName: '三',
});

// 删除数据
await dataSource.remove('1');

// 删除多条数据
await dataSource.remove(['1', '2', '3']);

以上操作默认均会修改dataSource.items。如果不需要更新,则可以指定函数的第二个参数为false,如:

const user = await dataSource.get('1', false);