Skip to content

Commit

Permalink
♻️ refactor: 基于 StoreUpdater 重构受控模式代码
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Mar 8, 2022
1 parent 89307f0 commit 337168e
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 139 deletions.
8 changes: 0 additions & 8 deletions packages/sortable-list/src/components/Action/index.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@focused-outline-color: #4c9ffe;

.Action {
display: flex;
width: 12px;
Expand Down Expand Up @@ -41,10 +39,4 @@
fill: var(--fill, #788491);
}
}

&:focus-visible {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0),
0 0px 0px 2px @focused-outline-color;
}
}
2 changes: 0 additions & 2 deletions packages/sortable-list/src/components/BaseItem/index.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
@import (reference) '~antd/es/style/themes/default.less';

@border-color: #efefef;
@handle-color: rgba(0, 0, 0, 0.25);
@box-shadow-border: 0 0 0 calc(1px / var(--scale-x, 1)) hsla(240, 0%, 26%, 0.05);
@box-shadow-common: 0 1px calc(3px / var(--scale-x, 1)) hsla(240, 0%, 22%, 0.15);
@box-shadow: @box-shadow-border, @box-shadow-common;
Expand Down
1 change: 1 addition & 0 deletions packages/sortable-list/src/components/SortableItem.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import type { FC } from 'react';
import React from 'react';

Expand Down
31 changes: 3 additions & 28 deletions packages/sortable-list/src/container/StoreUpdater.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
import { useEffect } from 'react';

import { useStore } from '../store';
import type {
StoreUpdaterProps,
SortableItemList,
OnDataChange,
} from '../types';

const useStoreUpdater = <T extends any>(
value: T | undefined,
setStoreState: (param: T) => void,
) => {
useEffect(() => {
if (typeof value !== 'undefined') {
setStoreState(value);
}
}, [value]);
};
import { useStoreUpdater } from '../hooks/useStoreUpdater';
import type { StoreUpdaterProps } from '../types';

const StoreUpdater = (props: StoreUpdaterProps) => {
const syncOutsideProps = useStore((s) => s.syncOutsideProps);

useStoreUpdater<SortableItemList>(props.data, (data) => {
syncOutsideProps({ data });
});

useStoreUpdater<OnDataChange>(props.onDataChange, (fn) => {
syncOutsideProps({ onDataChange: fn });
});
useStoreUpdater(props);

return null;
};
Expand Down
97 changes: 0 additions & 97 deletions packages/sortable-list/src/hooks/useSortableList.ts

This file was deleted.

137 changes: 137 additions & 0 deletions packages/sortable-list/src/hooks/useStoreUpdater.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { useCallback, useState } from 'react';
import { act, renderHook } from '@testing-library/react-hooks';

import { useStoreUpdater } from './useStoreUpdater';
import type { SortableItemList } from '../types';
import { createStore, Provider, useStore } from '../store';

const renderOptions = {
wrapper: ({ children }) => (
<Provider createStore={createStore}>{children}</Provider>
),
};
describe('useStoreUpdater', () => {
it('外部控制初始值', () => {
const { result } = renderHook(() => {
useStoreUpdater({ defaultData: [{ id: '123' }] });

return useStore((s) => s.data);
}, renderOptions);
expect(result.current).toEqual([{ id: '123' }]);
});

it('外部设置值', () => {
const { result } = renderHook(() => {
useStoreUpdater({ data: [{ id: '123' }] });
return useStore((s) => s.data);
}, renderOptions);

expect(result.current).toEqual([{ id: '123' }]);
});

it('外部 setValue', () => {
const controlledValue: SortableItemList = [
{ id: '1' },
{ id: '2' },
{ id: '3' },
];

const { result } = renderHook(() => {
const [value, setConfig] = useState<SortableItemList>();
useStoreUpdater({
data: value,
});

const data = useStore((s) => s.data);
return { data, setConfig };
}, renderOptions);

act(() => {
result.current.setConfig(controlledValue);
});

expect(result.current.data).toEqual([
{ id: '1' },
{ id: '2' },
{ id: '3' },
]);
});

it('没有 onChange 时不更改', () => {
const useNoOnChange = () => {
const [data] = useState([{ id: '1' }]);
useStoreUpdater({ data: data });

const store = useStore();
return { store, data };
};
const { result } = renderHook(() => useNoOnChange(), renderOptions);
//
//
act(() => {
result.current.store.addItem({ id: '333' });
});

expect(result.current.data).toEqual([{ id: '1' }]);
});

it('内部修改值 外部 value 更新', () => {
const { result } = renderHook(() => {
const [value, setConfig] = useState<SortableItemList>([
{ id: '1' },
{ id: '2' },
{ id: '3' },
]);

const func = useCallback((data) => {
console.log('xuga', data);
setConfig(data);
}, []);

useStoreUpdater({
data: value,
onDataChange: func,
});

const reorder = useStore((s) => s.reorder);

return { value, reorder };
}, renderOptions);

expect(result.current.value).toEqual([
{ id: '1' },
{ id: '2' },
{ id: '3' },
]);

act(() => {
result.current.reorder(2, 0);
});

expect(result.current.value).toEqual([
{ id: '3' },
{ id: '1' },
{ id: '2' },
]);

act(() => {
result.current.reorder(1, 0);
});

expect(result.current.value).toEqual([
{ id: '1' },
{ id: '3' },
{ id: '2' },
]);

act(() => {
result.current.reorder(1, 1);
});

expect(result.current.value).toEqual([
{ id: '1' },
{ id: '3' },
{ id: '2' },
]);
});
});
35 changes: 35 additions & 0 deletions packages/sortable-list/src/hooks/useStoreUpdater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect } from 'react';
import type { StoreUpdaterProps } from '../types';
import { useStore } from '../store';
import shallow from 'zustand/shallow';

export const useStoreUpdater = ({
data,
defaultData,
onDataChange,
}: StoreUpdaterProps) => {
const { syncOnDataChange, syncOutsideData } = useStore(
(s) => ({
syncOutsideData: s.syncOutsideData,
syncOnDataChange: s.syncOnDataChange,
}),
shallow,
);

useEffect(() => {
if (defaultData) {
syncOutsideData(defaultData);
}
}, []);

useEffect(() => {
if (!data) return;
syncOutsideData(data);
}, [data]);

useEffect(() => {
if (!onDataChange) return;

syncOnDataChange(onDataChange);
}, [onDataChange]);
};
27 changes: 27 additions & 0 deletions packages/sortable-list/src/store/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,31 @@ describe('useStore', () => {
{ id: '2' },
]);
});

it('激活/取消激活 item', () => {
const { result } = renderHook(() => useStore());

expect(result.current.activeId).toBeNull();

// 设置
act(() => {
result.current.internalUpdateData([
{ id: '1' },
{ id: '2' },
{ id: '3' },
]);
});
// 激活
act(() => {
result.current.activateItem('2');
});
expect(result.current.activeId).toEqual('2');

// 取消激活

act(() => {
result.current.deactivateItem();
});
expect(result.current.activeId).toEqual(null);
});
});
12 changes: 9 additions & 3 deletions packages/sortable-list/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import create from 'zustand';
import createContext from 'zustand/context';

import { arrayMove } from '@dnd-kit/sortable';
import isEqual from 'lodash.isequal';
import produce from 'immer';
import initialState from './initialState';
import type { SortableListStore } from '../types';

const createStore = () =>
create<SortableListStore>((set, get) => ({
// 内部值
Expand All @@ -26,8 +26,14 @@ const createStore = () =>
}
},

syncOutsideProps: (props) => {
set({ onDataChange: props.onDataChange, data: props.data ?? get().data });
syncOnDataChange: (onDataChange) => {
set({ onDataChange });
},

syncOutsideData: (data) => {
if (isEqual(get().data, data)) return;

set({ data });
},

// 重新排序
Expand Down
Loading

0 comments on commit 337168e

Please sign in to comment.