Skip to content

Commit bfb8218

Browse files
committed
feat: Add misc hooks
1 parent 8f57976 commit bfb8218

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

src/hooks/useMemo.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from 'react';
2+
3+
interface Cache<Value, Condition> {
4+
condition?: Condition;
5+
value?: Value;
6+
}
7+
8+
export default function useMemo<Value, Condition = any[]>(
9+
getValue: () => Value,
10+
condition: Condition,
11+
shouldUpdate: (prev: Condition, next: Condition) => boolean,
12+
) {
13+
const cacheRef = React.useRef<Cache<Value, Condition>>({});
14+
15+
if (
16+
!('value' in cacheRef.current) ||
17+
shouldUpdate(cacheRef.current.condition, condition)
18+
) {
19+
cacheRef.current.value = getValue();
20+
cacheRef.current.condition = condition;
21+
}
22+
23+
return cacheRef.current.value;
24+
}

src/hooks/useMergedState.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from 'react';
2+
3+
export default function useControlledState<T, R = T>(
4+
defaultStateValue: T | (() => T),
5+
option?: {
6+
defaultValue?: T | (() => T);
7+
value?: T;
8+
onChange?: (value: T, prevValue: T) => void;
9+
postState?: (value: T) => T;
10+
},
11+
): [R, (value: T) => void] {
12+
const { defaultValue, value, onChange, postState } = option || {};
13+
const [innerValue, setInnerValue] = React.useState<T>(() => {
14+
if (value !== undefined) {
15+
return value;
16+
}
17+
if (defaultValue !== undefined) {
18+
return typeof defaultValue === 'function'
19+
? (defaultValue as any)()
20+
: defaultValue;
21+
}
22+
return typeof defaultStateValue === 'function'
23+
? (defaultStateValue as any)()
24+
: defaultStateValue;
25+
});
26+
27+
let mergedValue = value !== undefined ? value : innerValue;
28+
if (postState) {
29+
mergedValue = postState(mergedValue);
30+
}
31+
32+
function triggerChange(newValue: T) {
33+
setInnerValue(newValue);
34+
if (mergedValue !== newValue && onChange) {
35+
onChange(newValue, mergedValue);
36+
}
37+
}
38+
39+
return [(mergedValue as unknown) as R, triggerChange];
40+
}

tests/hooks.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as React from 'react';
2+
import { mount } from 'enzyme';
3+
import useMemo from '../src/hooks/useMemo';
4+
5+
describe('hooks', () => {
6+
it('useMemo', () => {
7+
const FC = ({ open, data }) => {
8+
const memoData = useMemo(
9+
() => data,
10+
[open, data],
11+
(prev, next) => next[0] && prev[1] !== next[1],
12+
);
13+
return <div memoData={memoData} />;
14+
};
15+
16+
const wrapper = mount(<FC data="open" open />);
17+
expect(wrapper.find('div').props().memoData).toEqual('open');
18+
19+
wrapper.setProps({ data: 'again' });
20+
expect(wrapper.find('div').props().memoData).toEqual('again');
21+
22+
wrapper.setProps({ data: 'close', open: false });
23+
expect(wrapper.find('div').props().memoData).toEqual('again');
24+
25+
wrapper.setProps({ data: 'repeat', open: true });
26+
expect(wrapper.find('div').props().memoData).toEqual('repeat');
27+
});
28+
});

0 commit comments

Comments
 (0)