Skip to content
This repository has been archived by the owner on Jun 10, 2020. It is now read-only.

Commit

Permalink
feat: support selector (#13)
Browse files Browse the repository at this point in the history
* feat: add optional selector support

* chore: remove unnecessary code

* chore: use lodash.isequal to filter changes

* test: add test cases
  • Loading branch information
ttys026 authored and sorrycc committed Dec 10, 2019
1 parent 059d7c9 commit 68bfcbc
Show file tree
Hide file tree
Showing 21 changed files with 130 additions and 15 deletions.
10 changes: 6 additions & 4 deletions example/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React from 'react';
import { useModel, Models } from 'umi';

export default () => {
const { counter, increment, decrement } = useModel('counter');
const fullRet = useModel('counter');
const partialRet = useModel('counter', c => c.counter);

return (<>
{counter}
<button onClick={increment}>add</button>
<button onClick={decrement}>minus</button>
{partialRet}
<button onClick={fullRet.increment}>add</button>
<button onClick={fullRet.decrement}>minus</button>
</>);
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"src"
],
"dependencies": {
"globby": "7.1.1"
"globby": "7.1.1",
"lodash.isequal": "^4.5.0"
}
}
2 changes: 2 additions & 0 deletions src/helpers/isEqual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @ts-ignore
export { default as isEqual } from 'lodash.isequal';
35 changes: 26 additions & 9 deletions src/utils/getUseModelContent.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
import { join } from 'path';

export default function() {
return `
import { useState, useEffect, useContext } from 'react';
//@ts-ignore
return `import { useState, useEffect, useContext, useRef } from 'react';
import { UmiContext } from '${join(__dirname, '..', 'helpers', 'constant')}';
import { Model } from './Provider';
import { Model } from './provider';
import { isEqual } from 'lodash';
export const useModel = <T extends keyof Model<T>>(namespace: T) : Model<T>[T] => {
export function useModel<T extends keyof Model<T>>(model: T): Model<T>[T]
export function useModel<T extends keyof Model<T>, U>(model: T, selector: (model: Model<T>[T]) => U): U
export function useModel<T extends keyof Model<T>, U>(
namespace: T,
updater?: (model: Model<T>[T]) => U
) : typeof updater extends undefined ? Model<T>[T] : ReturnType<NonNullable<typeof updater>>{
type RetState = typeof updater extends undefined ? Model<T>[T] : ReturnType<NonNullable<typeof updater>>
const dispatcher = useContext<any>(UmiContext);
const [state, setState] = useState<Model<T>[T]>(
() => dispatcher.data![namespace] as Model<T>[T]
const updaterRef = useRef(updater);
updaterRef.current = updater;
const [state, setState] = useState<RetState>(
() => updaterRef.current ? updaterRef.current(dispatcher.data![namespace]) : dispatcher.data![namespace]
);
useEffect(() => {
const handler = (e: any) => {
setState(e);
if(updater && updaterRef.current){
const ret = updaterRef.current(e);
if(!isEqual(ret, state)){
setState(ret);
}
} else {
setState(e);
}
}
try {
dispatcher.callbacks![namespace]!.add(handler);
Expand All @@ -26,7 +43,7 @@ export const useModel = <T extends keyof Model<T>>(namespace: T) : Model<T>[T] =
dispatcher.callbacks![namespace]!.delete(handler);
}
}, [namespace])
return state!;
return state;
};
`;
}
Binary file added test/fixtures/.DS_Store
Binary file not shown.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions test/fixtures/modelDNE/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { useModel } from './.umi/useModel';

export default () => {
const ret = useModel('user');
return (<>
<h2 data-testid="error">component should not break</h2>
<h2 data-testid="ret">{`ret is: ${ret}`}</h2>
</>);
}
5 changes: 5 additions & 0 deletions test/fixtures/modelDNE/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

export default async function ({ getByTestId }) {
expect(getByTestId('error').innerHTML).toEqual('component should not break');
expect(getByTestId('ret').innerHTML).toEqual('ret is: undefined');
}
11 changes: 11 additions & 0 deletions test/fixtures/modelWithDependencies/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { useModel } from './.umi/useModel';

export default () => {
const { switchRole } = useModel('auth');
const message = useModel('message');
return (<>
<h2 data-testid="message">{message}</h2>
<button onClick={switchRole}>switch</button>
</>);
}
7 changes: 7 additions & 0 deletions test/fixtures/modelWithDependencies/models/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useState } from 'react';

export default () => {
const [ role, setRole ] = useState('admin');
const switchRole = () => setRole(r => r === 'admin' ? 'user' : 'admin')
return { role, switchRole };
}
7 changes: 7 additions & 0 deletions test/fixtures/modelWithDependencies/models/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useState } from 'react';
import { useModel } from '../.umi/useModel';

export default () => {
const { role } = useModel('auth');
return role === 'admin' ? 'Hello admin' : 'Hello user';
}
8 changes: 8 additions & 0 deletions test/fixtures/modelWithDependencies/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

export default async function ({ getByTestId, getByText, fireEvent }) {
expect(getByTestId('message').innerHTML).toEqual('Hello admin');
fireEvent.click(getByText('switch'));
expect(getByTestId('message').innerHTML).toEqual('Hello user');
fireEvent.click(getByText('switch'));
expect(getByTestId('message').innerHTML).toEqual('Hello admin');
}
9 changes: 9 additions & 0 deletions test/fixtures/modelWithError/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { useModel } from './.umi/useModel';

export default () => {
const ret = useModel('name');
return (<>
<h2 data-testid="count">{`${ret}`}</h2>
</>);
}
6 changes: 6 additions & 0 deletions test/fixtures/modelWithError/models/name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useState } from 'react';

export default () => {
const data = {};
return { name: data.user.name };
}
6 changes: 6 additions & 0 deletions test/fixtures/modelWithError/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export default async function ({ getByTestId }) {
console.error = jest.fn();
// expect(console.error).toHaveBeenCalledTimes(1);
expect(getByTestId('count').innerHTML).toEqual('undefined');
}
9 changes: 9 additions & 0 deletions test/fixtures/modelWithSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { useModel } from './.umi/useModel';

export default () => {
const { isAdult, name } = useModel('user', user => ({ name: user.name, isAdult: user.age > 18 }));
return (<>
<h2 data-testid="user">{name} is {isAdult ? 'an adult' : 'a teen'}</h2>
</>);
}
11 changes: 11 additions & 0 deletions test/fixtures/modelWithSelector/models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useState } from 'react';

export default () => {
return {
name: 'Troy',
email: '[email protected]',
gender: 'male',
height: '6\'2',
age: 24,
}
}
4 changes: 4 additions & 0 deletions test/fixtures/modelWithSelector/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default async function ({ getByTestId }) {
expect(getByTestId('user').innerHTML).toEqual('Troy is an adult');
}
2 changes: 1 addition & 1 deletion test/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ readdirSync(fixtures)
const renderRet = render(
<Provider><App /></Provider>
);
await require(join(fixture, 'test.ts')).default({
await require(join(fixture, 'test.js')).default({
...renderRet,
fireEvent,
delay,
Expand Down

0 comments on commit 68bfcbc

Please sign in to comment.