Skip to content

Commit dc3aaaf

Browse files
authored
use typeless 1.0.0 (#4)
1 parent 27c6c1a commit dc3aaaf

37 files changed

+158
-701
lines changed

.blueprints/feature/{{name}}/interface.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import React from 'react';
22
import { DefaultSuspense } from 'src/components/DefaultSuspense';
33
import { RouteConfig } from 'src/types';
4-
import { createActions } from 'typeless';
4+
import { createModule } from 'typeless';
5+
import { {{pascalCase name}}Symbol } from './symbol';
56

6-
// --- Constants ---
7-
export const MODULE = '{{name}}';
87

98
// --- Actions ---
10-
export const {{pascalCase name}}Actions = createActions(MODULE, {});
9+
export const [handle, {{pascalCase name}}Actions, get{{pascalCase name}}State] = createModule({{pascalCase name}}Symbol)
10+
.withActions({})
11+
.withState<{{pascalCase name}}State >();
1112

1213
// --- Routing ---
1314
const ModuleLoader = React.lazy(() => import('./module'));
@@ -29,9 +30,3 @@ export const routeConfig: RouteConfig = {
2930
export interface {{pascalCase name}}State {
3031
foo: string;
3132
}
32-
33-
declare module 'typeless/types' {
34-
export interface DefaultState {
35-
{{name}}: {{pascalCase name}}State;
36-
}
37-
}
Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
import React from 'react';
2-
import { createEpic, createReducer, useModule } from 'typeless';
32
import { {{pascalCase name}}View } from './components/{{pascalCase name}}View';
4-
import { {{pascalCase name}}Actions, {{pascalCase name}}State, MODULE } from './interface';
3+
import { {{pascalCase name}}Actions, {{pascalCase name}}State, handle } from './interface';
54

65
// --- Epic ---
7-
export const epic = createEpic(MODULE);
6+
handle.epic();
87

98
// --- Reducer ---
109
const initialState: {{pascalCase name}}State = {
1110
foo: 'bar',
1211
};
1312

14-
export const reducer = createReducer(initialState);
13+
handle.reducer(initialState);
1514

1615
// --- Module ---
1716
export default () => {
18-
useModule({
19-
epic,
20-
reducer,
21-
reducerPath: ['{{name}}'],
22-
actions: {{pascalCase name}}Actions,
23-
});
17+
handle();
2418
return <{{pascalCase name}}View />;
2519
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const {{pascalCase name}}Symbol = Symbol('{{name}}');

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ yarn build
2525
- Logout button.
2626
- Loading the initial user instance on the page load. Routing is ignored if the initial data is not loaded. Similar to `onEnter` functionality from old `react-router`.
2727
- Routing
28-
- A very simple implementation with routing and redux. No need for `react-router`!
28+
- Implemented with `typeless-router`
2929
- Dynamic configuration. [`RouteResolver`](/src/components/RouteResolver.tsx) scans all modules and loads all routes automatically.
3030
- Example `RouteConfig`. Some routes are only for the authenticated user, and some routes are only for the anonymous user. Feel free to extend this functionality depending on your needs.
3131
- Lazy modules
3232
- Features `login`, `sample1`, `sample2` are dynamically loaded with `React.lazy`.
3333
- A loader is visible during lazy loading.
34-
- Example redux form.
35-
- [`form`](/src/form/createForm.ts) contains a custom library for Redux Form integration. This is WIP and probably will be extracted to a separate library.
34+
- Example form.
35+
- Implemented with `typeless-form`
3636

3737

3838

package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,18 @@
99
"@types/react-dom": "16.8.2",
1010
"@types/styled-components": "4.0.3",
1111
"@types/webpack-env": "^1.13.7",
12-
"history": "^4.9.0",
1312
"prettier": "^1.16.4",
1413
"react": "16.8.4",
1514
"react-app-rewired": "2.1.1",
1615
"react-dom": "16.8.4",
1716
"react-scripts": "3.0.1",
18-
"redux": "4.0.1",
19-
"redux-logger": "^3.0.6",
2017
"remeda": "^0.0.11",
2118
"rxjs": "6.2.2",
2219
"styled-components": "^4.1.3",
23-
"typeless": "0.0.18",
24-
"typescript": "^3.3.3333"
20+
"typeless": "^1.0.0",
21+
"typeless-form": "^1.0.0",
22+
"typeless-router": "^1.0.0",
23+
"typescript": "^3.5.1"
2524
},
2625
"scripts": {
2726
"prettier": "prettier --write 'src/**/*.{ts,tsx}'",

public/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
manifest.json provides metadata used when your web app is added to the
1212
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
1313
-->
14-
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
1514
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
1615
<!--
1716
Notice the use of %PUBLIC_URL% in the tags above.

src/components/App.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React from 'react';
2-
import * as R from 'remeda';
32
import { useGlobalModule } from 'src/features/global/module';
4-
import { useRouterModule } from 'src/features/router/module';
53
import { createGlobalStyle } from 'styled-components';
6-
import { useMappedState } from 'typeless';
74
import { RouteResolver } from './RouteResolver';
5+
import { useRouterModule } from 'src/features/router';
6+
import { getGlobalState } from 'src/features/global/interface';
87

98
const GlobalStyle = createGlobalStyle`
109
*, ::after, ::before {
@@ -29,9 +28,8 @@ const GlobalStyle = createGlobalStyle`
2928
export const App = () => {
3029
useRouterModule();
3130
useGlobalModule();
32-
const { isLoaded } = useMappedState(state =>
33-
R.pick(state.global, ['isLoaded'])
34-
);
31+
const { isLoaded } = getGlobalState.useState();
32+
3533
return (
3634
<>
3735
{isLoaded && <RouteResolver />}

src/components/FormInput.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import styled, { css } from 'styled-components';
33

4-
export interface FormInputProps
4+
export interface InputProps
55
extends React.InputHTMLAttributes<HTMLInputElement> {
66
className?: string;
77
label: string;
@@ -13,7 +13,7 @@ const Label = styled.label`
1313
margin-bottom: 8px;
1414
`;
1515

16-
const Input = styled.input`
16+
const StyledInput = styled.input`
1717
display: block;
1818
width: 100%;
1919
padding: 10px;
@@ -43,24 +43,24 @@ const Error = styled.div`
4343
color: #dc3545;
4444
`;
4545

46-
const _FormInput = (props: FormInputProps) => {
46+
const _Input = (props: InputProps) => {
4747
const { className, label, error, ...rest } = props;
4848
return (
4949
<div className={className}>
5050
<Label>{label}</Label>
51-
<Input {...rest} />
51+
<StyledInput {...rest} />
5252
{error && <Error>{error}</Error>}
5353
</div>
5454
);
5555
};
5656

57-
export const FormInput = styled(_FormInput)`
57+
export const Input = styled(_Input)`
5858
width: 100%;
5959
display: block;
6060
${props =>
6161
props.error &&
6262
css`
63-
${Input} {
63+
${StyledInput} {
6464
border-color: #dc3545;
6565
&:focus {
6666
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.25);

src/components/Link.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/components/ReduxInput.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React, { useContext } from 'react';
2-
import { FormContext } from 'src/form';
3-
import { FormInput, FormInputProps } from './FormInput';
2+
import { FormContext } from 'typeless-form';
3+
import { Input, InputProps } from './FormInput';
44

5-
interface ReduxFormControlProps extends FormInputProps {
5+
interface ReduxFormControlProps extends InputProps {
66
name: string;
77
}
88

9-
export const ReduxInput = (props: ReduxFormControlProps) => {
9+
export const FormInput = (props: ReduxFormControlProps) => {
1010
const { name, ...rest } = props;
1111
const data = useContext(FormContext);
1212
if (!data) {
@@ -15,7 +15,7 @@ export const ReduxInput = (props: ReduxFormControlProps) => {
1515
const hasError = data.touched[name] && !!data.errors[name];
1616
const value = data.values[name];
1717
return (
18-
<FormInput
18+
<Input
1919
value={value == null ? '' : value}
2020
error={hasError ? data.errors[name] : null}
2121
onBlur={() => data.actions.blur(name)}

src/components/RouteResolver.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { useEffect, useState } from 'react';
22
import * as R from 'remeda';
3-
import { RouterActions, RouterLocation } from 'src/features/router/interface';
43
import { usePrevious } from 'src/hooks/usePrevious';
54
import { RouteConfig } from 'src/types';
65
import { useActions, useMappedState } from 'typeless';
6+
import { getRouterState, RouterActions, RouterLocation } from 'typeless-router';
7+
import { getGlobalState } from 'src/features/global/interface';
78

89
// load dynamically all routes from all interfaces
910
const req = require.context('../features', true, /interface.tsx?$/);
@@ -27,10 +28,13 @@ function getMatch(loc: RouterLocation | null, isLogged: boolean) {
2728
}
2829

2930
export const RouteResolver = () => {
30-
const { user, location } = useMappedState(state => ({
31-
...R.pick(state.global, ['isLoaded', 'user']),
32-
...R.pick(state.router, ['location']),
33-
}));
31+
const { user, location } = useMappedState(
32+
[getGlobalState, getRouterState],
33+
(global, router) => ({
34+
...R.pick(global, ['isLoaded', 'user']),
35+
...R.pick(router, ['location']),
36+
})
37+
);
3438
const { push } = useActions(RouterActions);
3539
const [component, setComponent] = useState(<div />);
3640

src/features/global/interface.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
import { User } from 'src/types';
2-
import { createActions } from 'typeless';
2+
import { createModule } from 'typeless';
3+
import { GlobalSymbol } from './symbol';
34

4-
// --- Constants ---
55
export const MODULE = 'global';
66

7-
// --- Actions ---
8-
export const GlobalActions = createActions(MODULE, {
9-
$mounted: null,
10-
logout: null,
11-
loggedIn: (user: User | null) => ({ payload: { user } }),
12-
});
7+
export const [handle, GlobalActions, getGlobalState] = createModule(
8+
GlobalSymbol
9+
)
10+
.withActions({
11+
$mounted: null,
12+
logout: null,
13+
loggedIn: (user: User | null) => ({ payload: { user } }),
14+
})
15+
.withState<GlobalState>();
1316

14-
// --- Types ---
1517
export interface GlobalState {
1618
isLoaded: boolean;
1719
user: User | null;
1820
}
19-
20-
declare module 'typeless/types' {
21-
export interface DefaultState {
22-
global: GlobalState;
23-
}
24-
}

src/features/global/module.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import * as Rx from 'src/rx';
22
import { getUser } from 'src/services/API';
33
import { clearAccessToken, getAccessToken } from 'src/services/Storage';
4-
import { createEpic, createReducer, useModule } from 'typeless';
5-
import { RouterActions } from '../router/interface';
6-
import { GlobalActions, GlobalState, MODULE } from './interface';
4+
import { GlobalActions, GlobalState, handle } from './interface';
5+
import { RouterActions } from 'typeless-router';
76

87
// --- Epic ---
9-
export const epic = createEpic(MODULE)
8+
handle
9+
.epic()
1010
.on(GlobalActions.$mounted, () => {
1111
if (getAccessToken()) {
1212
return getUser().pipe(Rx.map(GlobalActions.loggedIn));
1313
}
14-
return GlobalActions.loggedIn(null);
14+
// TODO bug in typeless 1.0.0
15+
// doesn't invoke re-render
16+
return Rx.of(GlobalActions.loggedIn(null)).pipe(Rx.delay(0));
1517
})
1618
.on(GlobalActions.logout, () => {
1719
clearAccessToken();
@@ -24,7 +26,8 @@ const initialState: GlobalState = {
2426
user: null,
2527
};
2628

27-
export const reducer = createReducer(initialState)
29+
export const reducer = handle
30+
.reducer(initialState)
2831
.on(GlobalActions.loggedIn, (state, { user }) => {
2932
state.isLoaded = true;
3033
state.user = user;
@@ -34,10 +37,4 @@ export const reducer = createReducer(initialState)
3437
});
3538

3639
// --- Module ---
37-
export const useGlobalModule = () =>
38-
useModule({
39-
epic,
40-
reducer,
41-
reducerPath: ['global'],
42-
actions: GlobalActions,
43-
});
40+
export const useGlobalModule = handle;

src/features/global/symbol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const GlobalSymbol = Symbol('global');

0 commit comments

Comments
 (0)