Skip to content

Updated Guide and refactored playground project to work with the most recent TypeScript 3.7 and React & Redux type definitions. #193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@

## Checklist

* [ ] I have edited `README_SOURCE.md` (NOT `README.md`)
* [ ] I have run `sh ./generate-readme.sh` script to generate an updated `README.md` (alternatively you can run `node ./generate-readme.js`)

* [ ] I have read [CONTRIBUTING.md](https://github.com/piotrwitek/react-redux-typescript-guide/blob/master/CONTRIBUTING.md)
* [ ] I have edited `README_SOURCE.md` (NOT `README.md`)
* [ ] I have run CI script locally `npm run ci-check` to generate an updated `README.md`
* [ ] I have linked all related issues above
* [ ] I have rebased my branch

352 changes: 228 additions & 124 deletions README.md

Large diffs are not rendered by default.

187 changes: 119 additions & 68 deletions README_SOURCE.md

Large diffs are not rendered by default.

1,128 changes: 1,042 additions & 86 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"devDependencies": {
"@types/node": "8.5.1",
"all-contributors-cli": "5.4.0",
"all-contributors-cli": "6.9.3",
"doctoc": "1.4.0",
"husky": "1.3.1"
"husky": "3.0.9"
},
"scripts": {
"ci-check": "npm run readme:generate",
"doctoc": "doctoc --maxlevel=4 README_SOURCE.md",
"ci-check": "npm run doctoc && npm run readme:generate",
"doctoc": "doctoc --maxlevel=3 README_SOURCE.md",
"readme:generate": "node generate-readme",
"contributors:check": "all-contributors check",
"contributors:add": "all-contributors add",
Expand Down
8,454 changes: 5,919 additions & 2,535 deletions playground/package-lock.json

Large diffs are not rendered by default.

63 changes: 32 additions & 31 deletions playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,35 @@
"build-storybook": "build-storybook -s public"
},
"dependencies": {
"@types/jest": "24.0.11",
"@types/node": "11.13.8",
"@types/prop-types": "15.7.1",
"@types/react": "16.8.14",
"@types/react-dom": "16.8.4",
"@types/react-redux": "7.0.8",
"@types/react-router-dom": "4.3.2",
"@testing-library/react": "9.3.1",
"@types/jest": "24.0.21",
"@types/node": "12.12.5",
"@types/prop-types": "15.7.3",
"@types/react": "16.9.11",
"@types/react-dom": "16.9.3",
"@types/react-redux": "7.1.5",
"@types/react-router-dom": "5.1.2",
"axios": "^0.19.0",
"connected-react-router": "6.5.0",
"connected-react-router": "6.5.2",
"cuid": "2.1.6",
"prop-types": "15.7.2",
"react": "16.8.6",
"react-dom": "16.8.6",
"react-redux": "7.1.0",
"react-redux-typescript-scripts": "1.5.0",
"react-router-dom": "4.3.1",
"react-scripts": "3.0.1",
"react-testing-library": "7.0.0",
"redux": "4.0.1",
"redux-observable": "1.1.0",
"react": "16.11.0",
"react-dom": "16.11.0",
"react-redux": "7.1.1",
"react-redux-typescript-scripts": "1.6.2",
"react-router-dom": "5.1.2",
"react-scripts": "3.2.0",
"redux": "4.0.4",
"redux-observable": "1.2.0",
"redux-thunk": "2.3.0",
"reselect": "4.0.0",
"rxjs": "6.5.1",
"tslib": "1.9.3",
"tslint": "5.16.0",
"typesafe-actions": "4.2.0",
"typescript": "3.4.5",
"utility-types": "3.5.0"
"rxjs": "6.5.3",
"tslib": "1.10.0",
"tslint": "5.20.0",
"tslint-react": "4.1.0",
"typesafe-actions": "5.1.0",
"typescript": "3.7.1-rc",
"utility-types": "3.9.0"
},
"browserslist": [
">0.2%",
Expand All @@ -57,14 +58,14 @@
"not op_mini all"
],
"devDependencies": {
"@storybook/addon-actions": "5.1.9",
"@storybook/addon-links": "5.1.9",
"@storybook/addon-storyshots": "5.1.9",
"@storybook/addons": "5.1.9",
"@storybook/react": "5.1.9",
"@types/storybook__addon-storyshots": "4.0.1",
"@storybook/addon-actions": "5.2.5",
"@storybook/addon-links": "5.2.5",
"@storybook/addon-storyshots": "5.2.5",
"@storybook/addons": "5.2.5",
"@storybook/react": "5.2.5",
"@types/storybook__addon-storyshots": "5.1.1",
"@types/storybook__react": "4.0.2",
"react-test-renderer": "16.8.6",
"require-context.macro": "1.0.4"
"react-test-renderer": "16.11.0",
"require-context.macro": "1.2.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ export class ClassCounterWithDefaultProps extends React.Component<
count: this.props.initialCount,
};

componentWillReceiveProps({ initialCount }: Props) {
if (initialCount != null && initialCount !== this.props.initialCount) {
this.setState({ count: initialCount });
}
}

handleIncrement = () => {
this.setState({ count: this.state.count + 1 });
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { render, fireEvent, cleanup } from 'react-testing-library';
import { render, fireEvent, cleanup } from '@testing-library/react';

import { FCCounterConnectedOwnProps as ConnectedCounter } from './fc-counter-connected-own-props';

Expand Down
32 changes: 18 additions & 14 deletions playground/src/features/counters/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ import { action } from 'typesafe-actions';

import { ADD, INCREMENT } from './constants';

// CLASSIC API
/* SIMPLE API */

export const increment = () => action(INCREMENT);
export const add = (amount: number) => action(ADD, amount);

// ALTERNATIVE API - allow to use reference to "action-creator" function instead of "type constant"
// e.g. case getType(increment): return { ... }
/* ADVANCED API */

// More flexible allowing to create complex actions more easily
// use can use "action-creator" instance in place of "type constant"
// e.g. case getType(increment): return action.payload;
// This will allow to completely eliminate need for "constants" in your application, more info here:
// https://github.com/piotrwitek/typesafe-actions#constants

// OPTION 1 (with generics):
// import { createStandardAction } from 'typesafe-actions';
// export const increment = createStandardAction(INCREMENT)<void>();
// export const add = createStandardAction(ADD)<number>();

// OPTION 2 (with resolve callback):
// import { createAction } from 'typesafe-actions';
// export const increment = createAction(INCREMENT);
// export const add = createAction(ADD, resolve => {
// return (amount: number) => resolve(amount);
// });
import { createAction } from 'typesafe-actions';
import { Todo } from '../todos/models';

export const emptyAction = createAction(INCREMENT)<void>();
export const payloadAction = createAction(ADD)<number>();
export const payloadMetaAction = createAction(ADD)<number, string>();

export const payloadCreatorAction = createAction(
'TOGGLE_TODO',
(todo: Todo) => todo.id
)<string>();
19 changes: 7 additions & 12 deletions playground/src/features/todos-typesafe/actions.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import cuid from 'cuid';
import { createStandardAction } from 'typesafe-actions';
import { createAction } from 'typesafe-actions';

import { TodosFilter, Todo } from './models';

const ADD = 'todos/ADD';
const TOGGLE = 'todos/TOGGLE';
const CHANGE_FILTER = 'todos/CHANGE_FILTER';

export const add = createStandardAction(ADD).map(
(payload: { title: string }) => ({
payload: {
...payload,
id: cuid(),
completed: false,
} as Todo,
})
);
export const add = createAction(ADD, (title: string) => ({
id: cuid(),
title,
}))<Todo>();

export const toggle = createStandardAction(TOGGLE)<{ id: string }>();
export const toggle = createAction(TOGGLE)<{ id: string }>();

export const changeFilter = createStandardAction(CHANGE_FILTER)<TodosFilter>();
export const changeFilter = createAction(CHANGE_FILTER)<TodosFilter>();
6 changes: 3 additions & 3 deletions playground/src/features/todos/reducer-ta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ const initialState: TodosState = {
};

const todos = createReducer(initialState.todos)
.handleAction(ADD, (state, action) => [...state, action.payload])
.handleAction(TOGGLE, (state, action) =>
.handleType(ADD, (state, action) => [...state, action.payload])
.handleType(TOGGLE, (state, action) =>
state.map(item =>
item.id === action.payload
? { ...item, completed: !item.completed }
: item
)
);

const todosFilter = createReducer(initialState.todosFilter).handleAction(
const todosFilter = createReducer(initialState.todosFilter).handleType(
CHANGE_FILTER,
(state, action) => action.payload
);
Expand Down
1 change: 1 addition & 0 deletions playground/src/hoc/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './with-connected-count';
export * from './with-error-boundary';
export * from './with-state';
59 changes: 59 additions & 0 deletions playground/src/hoc/with-connected-count.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { RootState } from 'MyTypes';
import React from 'react';
import { connect } from 'react-redux';
import { Diff } from 'utility-types';
import { countersActions, countersSelectors } from '../features/counters';

// These props will be injected into the base component
interface InjectedProps {
count: number;
onIncrement: () => void;
}

export const withConnectedCount = <BaseProps extends InjectedProps>(
BaseComponent: React.ComponentType<BaseProps>
) => {
type HocProps = Diff<BaseProps, InjectedProps> & {
// here you can extend hoc with new props
initialCount?: number;
};

const mapStateToProps = (state: RootState) => ({
count: countersSelectors.getReduxCounter(state.counters),
});

const dispatchProps = {
onIncrement: countersActions.increment,
};

class Hoc extends React.Component<InjectedProps> {
// Enhance component name for debugging and React-Dev-Tools
static displayName = `withConnectedCount(${BaseComponent.name})`;
// reference to original wrapped component
static readonly WrappedComponent = BaseComponent;

render() {
const { count, onIncrement, ...restProps } = this.props;

return (
<BaseComponent
count={count} // injected
onIncrement={onIncrement} // injected
{...(restProps as BaseProps)}
/>
);
}
}

const ConnectedHoc = connect<
ReturnType<typeof mapStateToProps>,
typeof dispatchProps,
HocProps,
RootState
>(
mapStateToProps,
dispatchProps
)(Hoc);

return ConnectedHoc;
};
10 changes: 10 additions & 0 deletions playground/src/hoc/with-connected-count.usage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from 'react';

import { withConnectedCount } from '../hoc';
import { FCCounter } from '../components';

const FCCounterWithConnectedCount = withConnectedCount(FCCounter);

export default () => (
<FCCounterWithConnectedCount initialCount={5} label={'FCCounterWithState'} />
);
28 changes: 5 additions & 23 deletions playground/src/hoc/with-error-boundary.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import * as React from 'react';
import { Subtract } from 'utility-types';
import React from 'react';

const MISSING_ERROR = 'Error was swallowed during propagation.';

// These props will be subtracted from base component props
interface InjectedProps {
onReset: () => void;
}

export const withErrorBoundary = <BaseProps extends InjectedProps>(
_BaseComponent: React.ComponentType<BaseProps>
export const withErrorBoundary = <BaseProps extends {}>(
BaseComponent: React.ComponentType<BaseProps>
) => {
// fix for TypeScript issues: https://github.com/piotrwitek/react-redux-typescript-guide/issues/111
const BaseComponent = _BaseComponent as React.ComponentType<InjectedProps>;

type HocProps = Subtract<BaseProps, InjectedProps> & {
type HocProps = {
// here you can extend hoc with new props
};
type HocState = {
Expand All @@ -40,21 +31,12 @@ export const withErrorBoundary = <BaseProps extends InjectedProps>(
// TODO: send error report to service provider
};

handleReset = () => {
this.setState({ error: undefined });
};

render() {
const { children, ...restProps } = this.props;
const { error } = this.state;

if (error) {
return (
<BaseComponent
onReset={this.handleReset} // injected
{...restProps}
/>
);
return <BaseComponent {...(restProps as BaseProps)} />;
}

return children;
Expand Down
15 changes: 6 additions & 9 deletions playground/src/hoc/with-state.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import * as React from 'react';
import { Subtract } from 'utility-types';
import React from 'react';
import { Diff } from 'utility-types';

// These props will be subtracted from base component props
// These props will be injected into the base component
interface InjectedProps {
count: number;
onIncrement: () => void;
}

export const withState = <BaseProps extends InjectedProps>(
_BaseComponent: React.ComponentType<BaseProps>
BaseComponent: React.ComponentType<BaseProps>
) => {
// fix for TypeScript issues: https://github.com/piotrwitek/react-redux-typescript-guide/issues/111
const BaseComponent = _BaseComponent as React.ComponentType<InjectedProps>;

type HocProps = Subtract<BaseProps, InjectedProps> & {
type HocProps = Diff<BaseProps, InjectedProps> & {
// here you can extend hoc with new props
initialCount?: number;
};
Expand Down Expand Up @@ -43,7 +40,7 @@ export const withState = <BaseProps extends InjectedProps>(
<BaseComponent
count={count} // injected
onIncrement={this.handleIncrement} // injected
{...restProps}
{...(restProps as BaseProps)}
/>
);
}
Expand Down
Loading