乐趣区

关于typescript:③reactahooks源码分析之useUrlState

剖析的源码来自 https://github.com/alibaba/ho…

useUrlState

简介

useUrlState 是一个通过 url query 来治理 state 的 Hook。url query,即网页 url 的问号前面跟着的一串属性值。该 hook 利用次要在以下两个方面:

  • 将组建的属性值晋升到 url 进行治理。
  • 利用该 hook 来治理 url 的 query。

useUrlState 能够做到不刷新页面即可扭转 query 参数,应用起来比拟不便。

useUrlState 有个比拟非凡的中央在于:应用它是须要独立装置 @ahooksjs/use-url-state 库的,源码中它也与其余 hook 寄存在了不同的地位。可能是因为 useUrlState 须要应用 react-router 和 query-string,因而按需下载,防止无意义的多下载几个库。

根本用法

它的用法比较简单,同 useSetState 相似,初始值都为对象中的键值对;应用如 setState(即 result 的第二个参数)传入须要扭转的键值对即可,也可传入函数;不同之处在于无奈减少属性值。

简略用例

该用例来自官网文档,实现了其根本用法:将状态同步到 url query 中。而后通过设置值为 undefined, 能够将属性复原为一开始传入的默认值。

import React from 'react';
import useUrlState from '@ahooksjs/use-url-state';

export default () => {const [state, setState] = useUrlState({count: '1'});

  return (
    <>
      <button
        style={{marginRight: 8}}
        type="button"
        onClick={() => setState({ count: Number(state.count || 0) + 1 })}
      >
        add
      </button>
      <button type="button" onClick={() => setState({ count: undefined})}>
        clear
      </button>
      <div>state: {state?.count}</div>
    </>
  );
};

源码剖析

首先是一些接口和类型的定义。

//... 省略局部

const rc = tmp as any;

//options 参数接口:export interface Options {
  navigateMode?: 'push' | 'replace';// 状态变更时切换 history 的形式
  parseOptions?: ParseOptions;//query-string parse 的配置
  stringifyOptions?: StringifyOptions;//query-string stringify 的配置
}

//parseOptions 的默认定义
const baseParseConfig: ParseOptions = {
  parseNumbers: false,
  parseBooleans: false,
};

//stringifyOptions 的默认定义
const baseStringifyConfig: StringifyOptions = {
  skipNull: false,
  skipEmptyString: false,
};

// 定义类型 UrlState 为 <string, any> 键值对
type UrlState = Record<string, any>;

对传入的参数进行了肯定的默认值合并解决,接着获取以后 url 的 location 对象,获取传入的心 state 值,辨别 react-router 的 5 或 6 版本来对新旧 state 值进行合并。

对步骤的具体解析写于代码中的正文局部。

const useUrlState = <S extends UrlState = UrlState>(initialState?: S | (() => S),
  options?: Options,
) => {type State = Partial<{ [key in keyof S]: any }>;
  const {navigateMode = 'push', parseOptions, stringifyOptions} = options || {};

  const mergedParseOptions = {...baseParseConfig, ...parseOptions};
  const mergedStringifyOptions = {...baseStringifyConfig, ...stringifyOptions};

//useLocation 返回示意以后页面的 location 对象。location 对象蕴含无关以后 URL 的信息。// 上面用到的有:search、hash

  const location = rc.useLocation();

  // react-router v5
  const history = rc.useHistory?.();
  // react-router v6
  const navigate = rc.useNavigate?.();

// 起到强制组件从新渲染的作用
  const update = useUpdate();

  const initialStateRef = useRef(typeof initialState === 'function' ? (initialState as () => S)() : initialState || {},
  );

// 当 location.search 扭转的时候,将 url 的 query 转换成对象
// 在本系列上一篇文章中曾经解释过 useMemo 的作用,有选择性地进行从新执行,缩小耗费。const queryFromUrl = useMemo(() => {return parse(location.search, mergedParseOptions);
  }, [location.search]);

// 当 queryFromUrl 扭转的时候,返回目前的 query 值
  const targetQuery: State = useMemo(() => ({
      ...initialStateRef.current,
      ...queryFromUrl,
    }),
    [queryFromUrl],
  );

// 获取传入的 newstate 值,并且反对传入函数
  const setState = (s: React.SetStateAction<State>) => {const newQuery = typeof s === 'function' ? s(targetQuery) : s;

    // 1. 如果 setState 后,search 没变动,就须要 update 来触发一次更新。// 2. update 和 history 的更新会合并,不会造成屡次更新。update();
    if (history) {history[navigateMode]({
        hash: location.hash,
        // 合并新旧 state,并利用 stringify 进行转换
        search: stringify({...queryFromUrl, ...newQuery}, mergedStringifyOptions) || '?',
      });
    }
    if (navigate) {
      navigate(
        {
          hash: location.hash,
          search: stringify({...queryFromUrl, ...newQuery}, mergedStringifyOptions) || '?',
        },
        {replace: navigateMode === 'replace',},
      );
    }
  };

  return [targetQuery, useMemoizedFn(setState)] as const;
};

export default useUrlState;

要点剖析

useLocation

useLocation 返回示意以后页面的 location 对象。location 对象蕴含无关以后 URL 的信息。
在该源码应用到的是 hash 和 search 信息。

  • hash:= 从井号 (#) 开始的 URL(锚)。该源码中间接获取了以后 url 的 hash 值,并未扭转。
  • search:= 从问号 (?) 开始的 URL(查问局部),相当于 query,也就是 useUrlState 重点解决的局部。

useUpdate

一个能够强制组件渲染的 hook,用法极其简略,const update = useUpdate(); 而后 update(); 即可。

parse, stringify

来自 query-string 库,作用是将对象和 url 的 query(如‘?name=abc&class=2’)这两种格局进行相互转换。

useRef

useRef 返回一个可变的 ref 对象,其.current 属性被初始化为传入的参数(initialValue),能够用来保留数据,返回的 ref 对象在组件的整个生命周期内继续存在。在这里用它来保留传入的初始值(以便当革除某个属性的时候,能够返回该属性的初始值)

参考资料:
https://cloud.tencent.com/dev…
https://react.docschina.org/d…

退出移动版