useStream refactoring (#40)
This commit is contained in:
@ -39,7 +39,7 @@
|
||||
"react-hooks/exhaustive-deps": [
|
||||
"warn",
|
||||
{
|
||||
"additionalHooks": "(useEqualMemo)"
|
||||
"additionalHooks": "(useEqualMemo|useStream)"
|
||||
}
|
||||
],
|
||||
"jsx-a11y/label-has-associated-control": 0,
|
||||
|
||||
@ -8,22 +8,31 @@ type User = {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
};
|
||||
|
||||
type Support = {
|
||||
text: string;
|
||||
url: string;
|
||||
};
|
||||
type UserReponse = {
|
||||
data: Array<User>;
|
||||
page: number;
|
||||
per_page: number;
|
||||
support: {
|
||||
text: string;
|
||||
url: string;
|
||||
}
|
||||
support: Support;
|
||||
total: number;
|
||||
total_pages: number;
|
||||
};
|
||||
|
||||
type UserFindResponse = {
|
||||
data: User;
|
||||
support: Support;
|
||||
}
|
||||
const ROOT_URL = 'https://reqres.in/api/users';
|
||||
export const usersApi = makeApi({
|
||||
request: async () => {
|
||||
const {data} = await http.get<void, UserReponse>('https://reqres.in/api/users');
|
||||
const {data} = await http.get<void, UserReponse>(ROOT_URL);
|
||||
return data;
|
||||
},
|
||||
findById: async (id: string) => {
|
||||
const {data} = await http.get<void, UserFindResponse>(`${ROOT_URL}/${id}`);
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, {ReactNode} from 'react';
|
||||
import {RemoteData, fold, map} from '@devexperts/remote-data-ts';
|
||||
import {RemoteData, fold, map as remoteDateMap} from '@devexperts/remote-data-ts';
|
||||
import {Stream} from '@most/types';
|
||||
import * as M from '@most/core';
|
||||
import {map} from '@most/core';
|
||||
import {pipe} from 'fp-ts/lib/pipeable';
|
||||
|
||||
export const renderAsyncData = <E, A>(
|
||||
@ -20,7 +20,7 @@ export const mapRD = <E, A, R>(mapper: (val: A) => R) => {
|
||||
return (stream$: Stream<RemoteData<E, A>>): Stream<RemoteData<E, R>> => {
|
||||
return pipe(
|
||||
stream$,
|
||||
M.map(val => map(mapper)(val))
|
||||
map(val => remoteDateMap(mapper)(val))
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,32 +1,33 @@
|
||||
import {Sink, Stream} from '@most/types';
|
||||
import {noop} from 'lodash';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {Stream, Sink} from '@most/types';
|
||||
import {newDefaultScheduler} from '@most/scheduler';
|
||||
import {pending, RemoteData} from '@devexperts/remote-data-ts';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const emptyFunc = () => {};
|
||||
|
||||
export const useStream = <T>(stream$: Stream<T>, defaultValue: T): T => {
|
||||
const [state, setState] = useState(defaultValue);
|
||||
|
||||
export function useStream<T extends Array<unknown>, R>(
|
||||
piping: () => Stream<R>,
|
||||
props: T,
|
||||
) {
|
||||
const [state, setState] = useState<R>();
|
||||
useEffect(() => {
|
||||
const sink: Sink<T> = {
|
||||
setState(undefined);
|
||||
// eslint-disable-next-line
|
||||
}, props);
|
||||
useEffect(() => {
|
||||
const effect$ = piping();
|
||||
const sink: Sink<R> = {
|
||||
event: (_, val) => {
|
||||
setState(val);
|
||||
},
|
||||
end: emptyFunc,
|
||||
error: emptyFunc
|
||||
end: noop,
|
||||
error: noop
|
||||
};
|
||||
const unsub = effect$.run(sink, newDefaultScheduler());
|
||||
|
||||
const effect$ = stream$.run(sink, newDefaultScheduler());
|
||||
return () => {
|
||||
effect$.dispose();
|
||||
unsub.dispose();
|
||||
};
|
||||
}, [stream$]);
|
||||
// eslint-disable-next-line
|
||||
}, props);
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export const useStreamRD = <T, E = Error>(stream$: Stream<RemoteData<E, T>>): RemoteData<E, T> => {
|
||||
return useStream(stream$, pending);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import React, {memo} from 'react';
|
||||
import {useStream} from '_utils/useStream';
|
||||
import {tasksService} from '_services/TasksService';
|
||||
|
||||
import {List, ListItem, ListItemIcon, ListItemText} from '@material-ui/core';
|
||||
import InboxIcon from '@material-ui/icons/Inbox';
|
||||
|
||||
const MainPage: React.FC = () => {
|
||||
const taskList = useStream(tasksService.stream$, []);
|
||||
|
||||
const taskList = useStream(() => tasksService.stream$, []);
|
||||
return (
|
||||
<List component="nav" aria-label="main mailbox folders">
|
||||
{taskList.map(task => (
|
||||
{taskList?.map(task => (
|
||||
<ListItem button key={task.id}>
|
||||
<ListItemIcon>
|
||||
<InboxIcon />
|
||||
|
||||
@ -1,16 +1,21 @@
|
||||
import React, {memo} from 'react';
|
||||
import {usersApi} from '../../../../core/api/usersTestApi';
|
||||
import {useStream} from '../../../../core/utils/useStream';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {usersApi} from '_api/usersTestApi';
|
||||
import {useStream} from '_utils/useStream';
|
||||
|
||||
const userList$ = usersApi.request();
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const users = useStream(userList$, []);
|
||||
const users = useStream(() => userList$, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
tags
|
||||
{users.map(user => (
|
||||
<div key={user.id}>{user.first_name}, {user.last_name}</div>
|
||||
{users?.map(user => (
|
||||
<div key={user.id}>
|
||||
{user.first_name}, {user.last_name}
|
||||
<span><Link to={`/tags/${user.id}`}> More info...</Link></span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
51
src/pages/tags/components/user/index.tsx
Normal file
51
src/pages/tags/components/user/index.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, {FC, memo} from 'react';
|
||||
import {pipe} from 'fp-ts/es6/pipeable';
|
||||
import {at, chain, periodic, map} from '@most/core';
|
||||
|
||||
import {useStream} from '_utils/useStream';
|
||||
import {usersApi} from '_api/usersTestApi';
|
||||
import {useParams} from 'react-router-dom';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const User: FC<Props> = () => {
|
||||
const {id} = useParams<Props>();
|
||||
|
||||
const user = useStream(() => {
|
||||
let i = 0;
|
||||
return pipe(
|
||||
at(3000, undefined),
|
||||
chain(() => usersApi.findById(id)),
|
||||
chain(data => {
|
||||
return pipe(periodic(1000), map(() => {
|
||||
i = i + 1;
|
||||
return {
|
||||
...data,
|
||||
chainableNumber: i
|
||||
};
|
||||
}));
|
||||
})
|
||||
);
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{user ? (
|
||||
<div>
|
||||
<div>{user.avatar}</div>
|
||||
<div>{user.email}</div>
|
||||
<div>{user.first_name}</div>
|
||||
<div>{user.id}</div>
|
||||
<div>{user.last_name}</div>
|
||||
<div>{user.chainableNumber}</div>
|
||||
</div>
|
||||
) : (
|
||||
'Loading...'
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(User);
|
||||
@ -1,8 +1,12 @@
|
||||
import React from 'react';
|
||||
import React, {Fragment} from 'react';
|
||||
import {Route} from 'react-router-dom';
|
||||
import {ROUTES} from '_consts/common';
|
||||
import Page from './components/page';
|
||||
import User from './components/user';
|
||||
|
||||
export default (
|
||||
<Fragment>
|
||||
<Route component={User} path={`${ROUTES.TAGS}/:id`} exact />
|
||||
<Route component={Page} path={ROUTES.TAGS} exact />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user