981377660LMT / ts

ts学习
6 stars 1 forks source link

react context 使用案例 #425

Open 981377660LMT opened 8 months ago

981377660LMT commented 8 months ago

import { createContext, useContext, useState } from 'react';

import { capitalize } from '../utils';

type VisibilityController<V extends object> = {
  [K in keyof V as `get${Capitalize<string & K>}`]: () => boolean;
} & {
  [K in keyof V as `set${Capitalize<string & K>}`]: (value: boolean) => void;
} & {
  [K in keyof V as `toggle${Capitalize<string & K>}`]: () => boolean;
};

/**
 * 用于控制多个组件的显示隐藏状态.
 * 一个group内部最多存在一个显示的组件.
 */
const useVisibilityController = <V extends Record<string, boolean>>(initVisibility: V): VisibilityController<V> => {
  const [groupVisibility, setGroupVisibility] = useState(initVisibility); // TODO, ref
  const getters = {};
  const setters = {};
  const toggles = {};

  Object.keys(groupVisibility).forEach((k) => {
    getters[`get${capitalize(k)}`] = (): boolean => groupVisibility[k];

    setters[`set${capitalize(k)}`] = (v: boolean) => {
      setGroupVisibility((pre) => ({ ...pre, [k]: v }));
    };

    toggles[`toggle${capitalize(k)}`] = (): boolean => {
      const flag = groupVisibility[k];
      setGroupVisibility((pre) => ({ ...pre, [k]: !flag }));
      return !flag;
    };
  });

  return { ...getters, ...setters, ...toggles } as any;
};

type FilterDropdownVisibility = {
  conjuctionDropdownVisible: boolean;
  columnDropdownVisible: boolean;
  operatorDropdownVisible: boolean;
  targetValueDropdownVisible: boolean;
  addFilterDropdownVisible: boolean;
};

type GroupDropdownVisibility = {
  columnDropdownVisible: boolean;
  addGroupDropdownVisible: boolean;
};

type SortDropdownVisibility = {
  columnDropdownVisible: boolean;
  addSortDropdownVisible: boolean;
};

const FilterDropdownVisibilityControllerContext = createContext<VisibilityController<FilterDropdownVisibility> | null>(null);
const GroupDropdownVisibilityControllerContext = createContext<VisibilityController<GroupDropdownVisibility> | null>(null);
const SortDropdownVisibilityControllerContext = createContext<VisibilityController<SortDropdownVisibility> | null>(null);

export const useFilterDropdownVisibilityControllerProvider = (initVisibility: FilterDropdownVisibility) => {
  const controller = useVisibilityController(initVisibility);
  return (props: React.PropsWithChildren<{}>) => {
    return <FilterDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
  };
};
export const useGroupDropdownVisibilityControllerProvider = (initVisibility: GroupDropdownVisibility) => {
  const controller = useVisibilityController(initVisibility);
  return (props: React.PropsWithChildren<{}>) => {
    return <GroupDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
  };
};
export const useSortDropdownVisibilityControllerProvider = (initVisibility: SortDropdownVisibility) => {
  const controller = useVisibilityController(initVisibility);
  return (props: React.PropsWithChildren<{}>) => {
    return <SortDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
  };
};

export const useFilterDropdownVisibilityController = () => useContext(FilterDropdownVisibilityControllerContext);
export const useGroupDropdownVisibilityController = () => useContext(GroupDropdownVisibilityControllerContext);
export const useSortDropdownVisibilityController = () => useContext(SortDropdownVisibilityControllerContext);
image
981377660LMT commented 7 months ago

这一版的动态版本挂载属性可读性太差,换一个实现。

981377660LMT commented 7 months ago

想清楚,对外暴露Context方便还是暴露Context.Provider方便

import { createContext, useContext, useState } from 'react';

/**
 * 用于控制多个组件的显示隐藏状态.
 * 一个group内部最多存在一个显示的组件.
 */
interface IVisibilityController<Id extends PropertyKey> {
  get: (id: Id) => boolean;
  set: (id: Id, visible: boolean) => void;
  toggle: (id: Id) => boolean;
  clear: () => void;
}

const useVisibilityController = <Id extends PropertyKey>(initVisibleId?: Id): IVisibilityController<Id> => {
  const NULL_ID = Symbol('NULL_ID');
  const [visibleId, setVisibleId] = useState<Id | Symbol>(initVisibleId ?? NULL_ID); // TODO, ref

  return {
    get (id) {
      return id === visibleId;
    },
    set (id, visible) {
      if (visible) {
        setVisibleId(id);
      } else if (id === visibleId) {
        setVisibleId(NULL_ID);
      }
    },
    toggle (id) {
      if (id === visibleId) {
        setVisibleId(NULL_ID);
        return false;
      } else {
        setVisibleId(id);
        return true;
      }
    },
    clear () {
      setVisibleId(NULL_ID);
    }
  };
};

type FilterDropdownId = `conjuctionDropdownVisible-${string | number}` | `columnDropdownVisible-${string | number}` | `operatorDropdownVisible-${string | number}` | `targetValueDropdownVisible-${string | number}` | 'addFilterDropdownVisible';
type GroupDropdownId = `columnDropdownVisible-${string | number}` | 'addGroupDropdownVisible';
type SortDropdownId = `columnDropdownVisible-${string | number}` | 'addSortDropdownVisible';

const FilterDropdownVisibilityControllerContext = createContext<IVisibilityController<FilterDropdownId> | null>(null);
const GroupDropdownVisibilityControllerContext = createContext<IVisibilityController<GroupDropdownId> | null>(null);
const SortDropdownVisibilityControllerContext = createContext<IVisibilityController<SortDropdownId> | null>(null);

export const FilterDropdownVisibilityControllerProvider = (props: React.PropsWithChildren<{}>) => {
  const controller = useVisibilityController();
  return <FilterDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
};
export const GroupDropdownVisibilityControllerProvider = (props: React.PropsWithChildren<{}>) => {
  const controller = useVisibilityController();
  return <GroupDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
};
export const useSortDropdownVisibilityControllerProvider = (props: React.PropsWithChildren<{}>) => {
  const controller = useVisibilityController();
  return <SortDropdownVisibilityControllerContext.Provider value={controller} {...props} />;
};

export const useFilterDropdownVisibilityController = () => useContext(FilterDropdownVisibilityControllerContext);
export const useGroupDropdownVisibilityController = () => useContext(GroupDropdownVisibilityControllerContext);
export const useSortDropdownVisibilityController = () => useContext(SortDropdownVisibilityControllerContext);