From 41130cb1a6ff8be41724971a050bf7d988d1448e Mon Sep 17 00:00:00 2001 From: Nico Peham Date: Sun, 28 Apr 2019 17:33:19 +0200 Subject: [PATCH 1/9] WIP: Redux (#9) Redux --- README.md | 2 + package.json | 6 +- src/configureStore.ts | 14 ++ src/index.tsx | 11 +- src/modules/user/reducer/user.reducer.ts | 17 ++ .../scenes/{ => userEdit}/UserEdit.scene.tsx | 6 +- .../userEditForm/UserEditForm.container.tsx | 45 ++++++ .../userEdit/userEditForm/UserEditForm.tsx | 20 +++ src/modules/user/user.actions.ts | 15 ++ src/modules/user/user.routing.ts | 5 +- src/rootReducer.ts | 4 + yarn.lock | 153 +++++++++++++++++- 12 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 src/configureStore.ts create mode 100644 src/modules/user/reducer/user.reducer.ts rename src/modules/user/scenes/{ => userEdit}/UserEdit.scene.tsx (66%) create mode 100644 src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx create mode 100644 src/modules/user/scenes/userEdit/userEditForm/UserEditForm.tsx create mode 100644 src/modules/user/user.actions.ts create mode 100644 src/rootReducer.ts diff --git a/README.md b/README.md index 17d8ae9..8849b7e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ * [react](https://github.com/facebook/react) * [react-router](https://github.com/ReactTraining/react-router) * [react-router-dom](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom) +* [redux](https://github.com/reduxjs/redux) +* [react-redux](https://github.com/reduxjs/react-redux) ## DevDependencies * [prettier](https://github.com/prettier/prettier) diff --git a/package.json b/package.json index 904664e..ce47470 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,13 @@ ] }, "dependencies": { + "deox": "^1.4.0", "react": "^16.4.2", "react-dom": "^16.4.2", + "react-redux": "^7.0.2", "react-router": "^4.3.1", - "react-router-dom": "^4.3.1" + "react-router-dom": "^4.3.1", + "redux": "^4.0.1" }, "devDependencies": { "@types/enzyme": "^3.9.0", @@ -36,6 +39,7 @@ "@types/jest": "^24.0.9", "@types/react": "^16.4.11", "@types/react-dom": "^16.0.7", + "@types/react-redux": "^7.0.8", "@types/react-router": "^4.4.4", "@types/react-router-dom": "^4.3.1", "enzyme": "^3.9.0", diff --git a/src/configureStore.ts b/src/configureStore.ts new file mode 100644 index 0000000..69b5f97 --- /dev/null +++ b/src/configureStore.ts @@ -0,0 +1,14 @@ +import { createStore, Dispatch } from 'redux'; + +import { rootReducer } from './rootReducer'; +import { UserState } from './modules/user/reducer/user.reducer'; + +export interface ConnectedReduxProps { + dispatch: Dispatch; +} + +export interface ApplicationState { + userState: UserState; +} + +export const store = createStore(rootReducer); diff --git a/src/index.tsx b/src/index.tsx index d7e4b45..6581329 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,15 +1,18 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; - +import { Provider } from 'react-redux'; declare let module: any; import { App } from './App'; +import { store } from './configureStore'; const RenderApp = () => ( - - - + + + + + ); ReactDOM.render(, document.getElementById('root') as HTMLElement); diff --git a/src/modules/user/reducer/user.reducer.ts b/src/modules/user/reducer/user.reducer.ts new file mode 100644 index 0000000..68ea035 --- /dev/null +++ b/src/modules/user/reducer/user.reducer.ts @@ -0,0 +1,17 @@ +import { User, createUser } from '../user.actions'; +import { createReducer } from 'deox'; + +export interface UserState { + user: User; +} + +const userDefaultState: UserState = { + user: { + firstName: 'john', + lastName: 'doe', + }, +}; + +export const userReducer = createReducer(userDefaultState, handle => [ + handle(createUser, (state, action) => ({ ...state, user: action.payload })), +]); diff --git a/src/modules/user/scenes/UserEdit.scene.tsx b/src/modules/user/scenes/userEdit/UserEdit.scene.tsx similarity index 66% rename from src/modules/user/scenes/UserEdit.scene.tsx rename to src/modules/user/scenes/userEdit/UserEdit.scene.tsx index c1eb011..09f81a0 100644 --- a/src/modules/user/scenes/UserEdit.scene.tsx +++ b/src/modules/user/scenes/userEdit/UserEdit.scene.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { RouteComponentProps } from 'react-router'; -import { SceneParams } from '../../../shared/types/routing'; +import { SceneParams } from '../../../../shared/types/routing'; +import { UserEditFormContainer } from './userEditForm/UserEditForm.container'; export interface UserEditSceneParams extends SceneParams { userId: string; @@ -16,6 +17,9 @@ export const UserEditSene: React.SFC< userId --> {userId}
projId --> {projId} +
+
+ ); }; diff --git a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx new file mode 100644 index 0000000..afadc13 --- /dev/null +++ b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; + +import { + ConnectedReduxProps, + ApplicationState, +} from '../../../../../configureStore'; +import { UserEditForm } from './UserEditForm'; +import { User, createUser } from '../../../user.actions'; + +type UserEditFormContainerStateMapProps = { + user: User; +}; + +const mapStateToProps = ( + state: ApplicationState, +): UserEditFormContainerStateMapProps => { + return { + user: state.userState.user, + }; +}; + +type RegisterFormContainerProps = ConnectedReduxProps & + UserEditFormContainerStateMapProps & { + passedProp: string; + }; + +export const UserEditFormContainer = connect(mapStateToProps)( + ({ dispatch, user, passedProp }: RegisterFormContainerProps) => { + const handleRegisterFormSubmit = (values: User) => { + // access to values + dispatch(createUser(values)); + }; + + return ( + <> +
passed: {passedProp}
+
user in state: {JSON.stringify(user)}
+
+
+ {' '} + + ); + }, +); diff --git a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.tsx b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.tsx new file mode 100644 index 0000000..5257f86 --- /dev/null +++ b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { User } from '../../../user.actions'; + +type UserEditFormProps = { + onSubmit: (userEdited: User) => void; +}; + +export const UserEditForm: React.SFC = ({ onSubmit }) => { + const userEdited: User = { + firstName: 'susan', + lastName: 'jackson', + }; + + const onUserEdited = () => onSubmit(userEdited); + return ( + <> + + + ); +}; diff --git a/src/modules/user/user.actions.ts b/src/modules/user/user.actions.ts new file mode 100644 index 0000000..d17f271 --- /dev/null +++ b/src/modules/user/user.actions.ts @@ -0,0 +1,15 @@ +import { createAction } from 'deox'; + +export type User = { + firstName: string; + lastName: string; +}; + +enum ActionTypeKeys { + CreateUser = 'USER::CREATE', +} + +export const createUser = createAction( + 'USER::CREATE', + resolve => (user: User) => resolve(user), +); diff --git a/src/modules/user/user.routing.ts b/src/modules/user/user.routing.ts index da89134..36f3fbc 100644 --- a/src/modules/user/user.routing.ts +++ b/src/modules/user/user.routing.ts @@ -1,6 +1,9 @@ -import { UserEditSene, UserEditSceneParams } from './scenes/UserEdit.scene'; import { UserListScene } from './scenes/UserList.scene'; import { AppRoute, IScene } from '../../shared/types/routing'; +import { + UserEditSene, + UserEditSceneParams, +} from './scenes/userEdit/UserEdit.scene'; export enum UserRoute { List = '/user/list', diff --git a/src/rootReducer.ts b/src/rootReducer.ts new file mode 100644 index 0000000..96952b2 --- /dev/null +++ b/src/rootReducer.ts @@ -0,0 +1,4 @@ +import { combineReducers } from 'redux'; +import { userReducer } from './modules/user/reducer/user.reducer'; + +export const rootReducer = combineReducers({ userState: userReducer }); diff --git a/yarn.lock b/yarn.lock index b88b0ab..8edef23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -112,6 +112,13 @@ dependencies: regenerator-runtime "^0.12.0" +"@babel/runtime@^7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc" + integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -376,6 +383,14 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" integrity sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q== +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/istanbul-lib-coverage@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a" @@ -415,6 +430,15 @@ dependencies: "@types/react" "*" +"@types/react-redux@^7.0.8": + version "7.0.8" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.0.8.tgz#c928863058e334d41031c6bedd0f113bc514e234" + integrity sha512-vIBC15E84ehN6RzdGwRVa41whp9e4CkfPm+WfD0r6y6vqBf4tQPKZeKEBfLLM8k79uSwQC7rh3rH/MFaN1IESQ== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + redux "^4.0.0" + "@types/react-router-dom@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.1.tgz#71fe2918f8f60474a891520def40a63997dafe04" @@ -1633,6 +1657,11 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.3.tgz#b701e5968245bf9b08d54ac83d00b624e622a9fa" integrity sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg== +curriable@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/curriable/-/curriable-1.2.5.tgz#fb6ff7ad8740927ea09db15d395d9d26ca9d2726" + integrity sha512-hQwrkCn8DNiCw5CG8OS0td2wfpCDtxo1dmrVYxCDWUBHQPkpAvN9RqBVbmC64oSQaBqPQD2SOCXcTWH1zXe2mA== + cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -1768,6 +1797,15 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +deox@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/deox/-/deox-1.4.0.tgz#7511c082cbe80ffee4c5095aa7efe0138d6057c4" + integrity sha512-J9lQse+5LG2/q4hZsjn5Jun2yJP2Al0OfAECQ1KtMXSh5eJYSRn6bh9XBTiBxxi4qymAb/ovC3HPdK760z4OAQ== + dependencies: + redux-starter-kit "0.4.3" + rxjs "^6.3.3" + tslib "^1.9.3" + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2280,6 +2318,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-equals@^1.2.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-1.6.2.tgz#f1762bde0d571b5e9d4e1c4ae0599792f6a6df5f" + integrity sha512-6FiKwHkrHIqdvo9I92yzCR/zuWE6iy5ldcOszStPbmo7Zzj3OoVeng++GE//JqO4i6JQ4vH/BVAfCmUCki+C3g== + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -2752,7 +2795,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== @@ -2882,6 +2925,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" +identitate@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/identitate/-/identitate-1.0.1.tgz#4b8bed39454f1d7a476e60ff0e4f82a094841dfc" + integrity sha512-xnDJ0JYhiZjBDuJRKbHoVzj5yP9FhATxLyUYswQyPdnJrwzGVBqS6DOmvKJi1lk7P+4dkL+hhUhuOZIcOUtG5A== + dependencies: + pathington "^1.0.1" + ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" @@ -2899,6 +2949,11 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +immer@^1.10.5: + version "1.12.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.12.1.tgz#40c6e5b292c00560836c2993bda3a24379d466f5" + integrity sha512-3fmKM6ovaqDt0CdC9daXpNi5x/YCYS3i4cwLdTVkhJdk5jrDXoPs7lCm3IqM3yhfSnz4tjjxbRG2CziQ7m8ztg== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -2958,7 +3013,7 @@ interpret@^1.1.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== -invariant@^2.2.4: +invariant@^2.1.0, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -3766,7 +3821,7 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -4891,6 +4946,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +pathington@^1.0.1, pathington@^1.1.5: + version "1.1.7" + resolved "https://registry.yarnpkg.com/pathington/-/pathington-1.1.7.tgz#caf2d2db899a31fea4e81e3657af6acde5171903" + integrity sha512-JxzhUzagDfNIOm4qqwQqP3rWeo7rNNOfIahy4n+3GTEdwXLqw5cJHUR0soSopQtNEv763lzxb6eA2xBllpR8zw== + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -5204,6 +5264,23 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2" integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA== +react-is@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + +react-redux@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.0.2.tgz#34b280a3482aaf60e7d4a504b1295165cbe6b86a" + integrity sha512-uKRuMgQt8dWbcz0U75oFK5tDo3boyAKrqvf/j94vpqRFFZfyDDy4kofUgloFIGyuKTq2Zz51zgK9RzOTFXk5ew== + dependencies: + "@babel/runtime" "^7.4.3" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.6" + react-router-dom@^4.3.1: version "4.4.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.4.0.tgz#fad67b46375f7081a76d1c92a83a92d28b5abc35" @@ -5318,6 +5395,44 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +redux-devtools-extension@^2.13.7: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + +redux-immutable-state-invariant@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-2.1.0.tgz#308fd3cc7415a0e7f11f51ec997b6379c7055ce1" + integrity sha512-3czbDKs35FwiBRsx/3KabUk5zSOoTXC+cgVofGkpBNv3jQcqIe5JrHcF5AmVt7B/4hyJ8MijBIpCJ8cife6yJg== + dependencies: + invariant "^2.1.0" + json-stringify-safe "^5.0.1" + +redux-starter-kit@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/redux-starter-kit/-/redux-starter-kit-0.4.3.tgz#66a583cde1f1995ae59782d87cc462260ca85fa8" + integrity sha512-T3HXtwEGyFLCy63QlZEM95rDzTQ5BKbBhhTINNVFD1yWgHBKtgN3tXw4/sX9+Ve1Y8mAtXbAhep/E7I0YVvZPg== + dependencies: + immer "^1.10.5" + redux "^4.0.0" + redux-devtools-extension "^2.13.7" + redux-immutable-state-invariant "^2.1.0" + redux-thunk "^2.2.0" + selectorator "^4.0.1" + +redux-thunk@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + +redux@^4.0.0, redux@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" + integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + reflect-metadata@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -5328,6 +5443,11 @@ regenerator-runtime@^0.12.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -5419,6 +5539,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -5575,6 +5700,16 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +selectorator@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/selectorator/-/selectorator-4.0.3.tgz#d33b11027da7e6838de277cd6eb1134125aa788b" + integrity sha512-A8+paRhzTab4Qm/38RAVnCgEZFbpn5xIWLyTCDqvyU3Obhmo94RS6UK1H00bVH7+U609sOhqbFJha09POsWURA== + dependencies: + fast-equals "^1.2.1" + identitate "^1.0.0" + reselect "^4.0.0" + unchanged "^2.0.1" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -6025,7 +6160,7 @@ supports-color@^6.0.0, supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -symbol-observable@^1.1.0: +symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -6230,7 +6365,7 @@ tslib@1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== -tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== @@ -6381,6 +6516,14 @@ uglify-js@3.4.x, uglify-js@^3.1.4: commander "~2.19.0" source-map "~0.6.1" +unchanged@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unchanged/-/unchanged-2.1.0.tgz#16308dde5bec15925032d91651f5c3843b957de6" + integrity sha512-CYVU0Mz35N821Pjxf+iG6JMPx/Yb6wStUq8uuhR52/+AU3vMiueOTrJ6u1vBKYSke7a75FAi2/jCdQkXcHaNGQ== + dependencies: + curriable "^1.2.4" + pathington "^1.1.5" + union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" From 819f5b2abe1f1a19c1afc4ba345587f32e151638 Mon Sep 17 00:00:00 2001 From: npeham Date: Sun, 28 Apr 2019 18:46:03 +0200 Subject: [PATCH 2/9] add redux-saga dependency --- package.json | 3 +- yarn.lock | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ce47470..b48d147 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "react-redux": "^7.0.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", - "redux": "^4.0.1" + "redux": "^4.0.1", + "redux-saga": "^1.0.2" }, "devDependencies": { "@types/enzyme": "^3.9.0", diff --git a/yarn.lock b/yarn.lock index 8edef23..feba418 100644 --- a/yarn.lock +++ b/yarn.lock @@ -105,6 +105,13 @@ dependencies: regenerator-runtime "^0.12.0" +"@babel/runtime@^7.0.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" + integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/runtime@^7.1.2": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" @@ -316,6 +323,50 @@ "@types/istanbul-lib-coverage" "^1.1.0" "@types/yargs" "^12.0.9" +"@redux-saga/core@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.0.2.tgz#4336a5bb4253e5ca69681c25a863fbbc03ea6d88" + integrity sha512-AsJYcpuYfM1cmxJvfhXs9HAFSZVEG17TMsLPlXH7+Hq5a5ZP4GqcbtijEmS2AC7NR5lLJHy8csxpqz22PeW5dw== + dependencies: + "@babel/runtime" "^7.0.0" + "@redux-saga/deferred" "^1.0.1" + "@redux-saga/delay-p" "^1.0.1" + "@redux-saga/is" "^1.0.2" + "@redux-saga/symbols" "^1.0.1" + "@redux-saga/types" "^1.0.2" + redux ">=0.10 <5" + typescript-tuple "^2.1.0" + +"@redux-saga/deferred@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.0.1.tgz#c895445e486bded90acf0b873b4e978fbfe458c2" + integrity sha512-+gW5xQ93QXOOmRLAmX8x2Hx1HpbTG6CM6+HcdTSbJovh4uMWaGyeDECnqXSt8QqA/ja3s2nqYXLqXFKepIQ1hw== + +"@redux-saga/delay-p@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.0.1.tgz#d69fc6103c7509ae80faa144ea17bbc69e51e029" + integrity sha512-0SnNDyDLUyB4NThtptAwiprNOnbCNhoed/Rp5JwS7SB+a/AdWynVgg/E6BmjsggLFNr07KW0bzn05tsPRBuU7Q== + dependencies: + "@redux-saga/symbols" "^1.0.1" + +"@redux-saga/is@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.0.2.tgz#7f4be014c97061898d7efb11d6c9de31e943ed38" + integrity sha512-WnaUOwYvPK2waWjzebT4uhL8zY76XNkzzpJ2EQJe8bN1tByvAjvT7MuJZTSshOhdHL5PsRO0MsH224XIXBJidQ== + dependencies: + "@redux-saga/symbols" "^1.0.1" + "@redux-saga/types" "^1.0.2" + +"@redux-saga/symbols@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.0.1.tgz#46512ae1275f88df061c42168d0f600ddb170c1e" + integrity sha512-akKkzcVnb1RzJaZV2LQFbi51abvdICMuAKwwLoCjjxLbLAGIw9EJxk5ucNnWSSCEsoEQMeol5tkAcK+Xzuv1Bg== + +"@redux-saga/types@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.0.2.tgz#1d94f02800b094753f9271c206a26c2a06ca14ee" + integrity sha512-8/qcMh15507AnXJ3lBeuhsdFwnWQqnp68EpUuHlYPixJ5vjVmls7/Jq48cnUlrZI8Jd9U1jkhfCl0gaT5KMgVw== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -5408,6 +5459,13 @@ redux-immutable-state-invariant@^2.1.0: invariant "^2.1.0" json-stringify-safe "^5.0.1" +redux-saga@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.0.2.tgz#0599703f975438f1b2f9d2b9a965ec58f0fdfcd7" + integrity sha512-dHV256by3eF2AnBPx1l3HqazQFkErZ82HDXgh4jSRpT72OrX31wyg8DA1q8+0HvENRfJAyhT/4qT5yH/vVqFfw== + dependencies: + "@redux-saga/core" "^1.0.2" + redux-starter-kit@0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/redux-starter-kit/-/redux-starter-kit-0.4.3.tgz#66a583cde1f1995ae59782d87cc462260ca85fa8" @@ -5425,7 +5483,7 @@ redux-thunk@^2.2.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== -redux@^4.0.0, redux@^4.0.1: +"redux@>=0.10 <5", redux@^4.0.0, redux@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== @@ -6498,6 +6556,25 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript-compare@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" + integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA== + dependencies: + typescript-logic "^0.0.0" + +typescript-logic@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196" + integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q== + +typescript-tuple@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" + integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q== + dependencies: + typescript-compare "^0.0.2" + typescript@^3.3.3333: version "3.3.3333" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6" From df6e56e10edfdc6eedea517532a59cb25fcb2da4 Mon Sep 17 00:00:00 2001 From: npeham Date: Sun, 28 Apr 2019 18:47:43 +0200 Subject: [PATCH 3/9] implement initial user.api --- src/modules/user/user.api.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/modules/user/user.api.ts diff --git a/src/modules/user/user.api.ts b/src/modules/user/user.api.ts new file mode 100644 index 0000000..4a14f95 --- /dev/null +++ b/src/modules/user/user.api.ts @@ -0,0 +1,6 @@ +import { User } from './user.actions'; + +export const userEditRequest = async (user: User): Promise => { + const fetchedUser: User = { firstName: 'ie', lastName: 'sei' }; + return fetchedUser; +}; From 8391018316376dda91b73d602cacd96c14637902 Mon Sep 17 00:00:00 2001 From: npeham Date: Sun, 28 Apr 2019 18:48:35 +0200 Subject: [PATCH 4/9] initial working(not everything type-safe) redux-saga setup --- src/configureStore.ts | 10 ++++++++-- src/modules/user/reducer/user.reducer.ts | 4 +++- src/modules/user/sagas/user.sagas.test.ts | 0 src/modules/user/sagas/user.sagas.ts | 18 +++++++++++++++++ .../userEditForm/UserEditForm.container.tsx | 4 ++-- src/modules/user/user.actions.ts | 20 +++++++++++++++++-- src/rootSaga.ts | 7 +++++++ 7 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 src/modules/user/sagas/user.sagas.test.ts create mode 100644 src/modules/user/sagas/user.sagas.ts create mode 100644 src/rootSaga.ts diff --git a/src/configureStore.ts b/src/configureStore.ts index 69b5f97..f6fae52 100644 --- a/src/configureStore.ts +++ b/src/configureStore.ts @@ -1,7 +1,9 @@ -import { createStore, Dispatch } from 'redux'; +import { createStore, Dispatch, applyMiddleware } from 'redux'; import { rootReducer } from './rootReducer'; import { UserState } from './modules/user/reducer/user.reducer'; +import { default as createSagaMiddleware } from '@redux-saga/core'; +import rootSaga from './rootSaga'; export interface ConnectedReduxProps { dispatch: Dispatch; @@ -11,4 +13,8 @@ export interface ApplicationState { userState: UserState; } -export const store = createStore(rootReducer); +const sagaMiddleware = createSagaMiddleware(); + +export const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); + +sagaMiddleware.run(rootSaga); diff --git a/src/modules/user/reducer/user.reducer.ts b/src/modules/user/reducer/user.reducer.ts index 68ea035..50082bc 100644 --- a/src/modules/user/reducer/user.reducer.ts +++ b/src/modules/user/reducer/user.reducer.ts @@ -13,5 +13,7 @@ const userDefaultState: UserState = { }; export const userReducer = createReducer(userDefaultState, handle => [ - handle(createUser, (state, action) => ({ ...state, user: action.payload })), + handle(createUser, (state, action) => { + return { ...state, user: action.payload }; + }), ]); diff --git a/src/modules/user/sagas/user.sagas.test.ts b/src/modules/user/sagas/user.sagas.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/user/sagas/user.sagas.ts b/src/modules/user/sagas/user.sagas.ts new file mode 100644 index 0000000..5fed64c --- /dev/null +++ b/src/modules/user/sagas/user.sagas.ts @@ -0,0 +1,18 @@ +import { takeLatest, call, put } from 'redux-saga/effects'; +import { + ActionTypeKeys, + createUserFailed, + createUserSucceeded, +} from '../user.actions'; + +export function* userEditSaga(action: any) { + try { + yield put(createUserSucceeded({ firstName: 'nico', lastName: 'peham' })); + } catch (error) { + yield put(createUserFailed()); + } +} + +export const userSagas = [ + takeLatest(ActionTypeKeys.CreateUserRequested, userEditSaga), +]; diff --git a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx index afadc13..ccdd162 100644 --- a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx +++ b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx @@ -6,7 +6,7 @@ import { ApplicationState, } from '../../../../../configureStore'; import { UserEditForm } from './UserEditForm'; -import { User, createUser } from '../../../user.actions'; +import { User, createUser, createUserRequested } from '../../../user.actions'; type UserEditFormContainerStateMapProps = { user: User; @@ -29,7 +29,7 @@ export const UserEditFormContainer = connect(mapStateToProps)( ({ dispatch, user, passedProp }: RegisterFormContainerProps) => { const handleRegisterFormSubmit = (values: User) => { // access to values - dispatch(createUser(values)); + dispatch(createUserRequested(values)); }; return ( diff --git a/src/modules/user/user.actions.ts b/src/modules/user/user.actions.ts index d17f271..0709383 100644 --- a/src/modules/user/user.actions.ts +++ b/src/modules/user/user.actions.ts @@ -5,11 +5,27 @@ export type User = { lastName: string; }; -enum ActionTypeKeys { +// https://redux.js.org/advanced/async-actions choose! +export enum ActionTypeKeys { + CreateUserRequested = 'USER::CREATE_REQUESTED', + CreateUserFailed = 'USER::CREATE_FAILED', + CreateUserSucceeded = 'USER::CREATE_SUCCEEDED', CreateUser = 'USER::CREATE', } export const createUser = createAction( - 'USER::CREATE', + ActionTypeKeys.CreateUser, resolve => (user: User) => resolve(user), ); + +export const createUserRequested = createAction( + ActionTypeKeys.CreateUserRequested, + resolve => (user: User) => resolve(user), +); + +export const createUserSucceeded = createAction( + ActionTypeKeys.CreateUserSucceeded, + resolve => (user: User) => resolve(user), +); + +export const createUserFailed = createAction(ActionTypeKeys.CreateUserFailed); diff --git a/src/rootSaga.ts b/src/rootSaga.ts new file mode 100644 index 0000000..03cb4f4 --- /dev/null +++ b/src/rootSaga.ts @@ -0,0 +1,7 @@ +import { userSagas } from './modules/user/sagas/user.sagas'; +import { all } from 'redux-saga/effects'; + +// https://github.com/redux-saga/redux-saga/issues/160#issuecomment-308540204 +export default function* rootSaga() { + yield all([...userSagas]); +} From 87a5890a29df8ee404d42bcc38e1868ee3a165e8 Mon Sep 17 00:00:00 2001 From: npeham Date: Sun, 28 Apr 2019 18:58:36 +0200 Subject: [PATCH 5/9] explicitly typed return value of generator --- src/modules/user/sagas/user.sagas.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/user/sagas/user.sagas.ts b/src/modules/user/sagas/user.sagas.ts index 5fed64c..b02bfad 100644 --- a/src/modules/user/sagas/user.sagas.ts +++ b/src/modules/user/sagas/user.sagas.ts @@ -3,11 +3,19 @@ import { ActionTypeKeys, createUserFailed, createUserSucceeded, + User, } from '../user.actions'; +import { userEditRequest } from '../user.api'; export function* userEditSaga(action: any) { try { - yield put(createUserSucceeded({ firstName: 'nico', lastName: 'peham' })); + // define type explicitly because of lack of return types of generators: + // https://github.com/redux-saga/redux-saga/issues/1286#issuecomment-482866473 + const fetchedUser: User = yield call(userEditRequest, { + firstName: 'nico', + lastName: 'peham', + }); + yield put(createUserSucceeded(fetchedUser)); } catch (error) { yield put(createUserFailed()); } From 590efb55243aafc069f07fae3e37abb5e4a4bafc Mon Sep 17 00:00:00 2001 From: npeham Date: Sun, 28 Apr 2019 19:40:38 +0200 Subject: [PATCH 6/9] create own type for (CreateUser)RequestedAction for better type support in saga --- src/modules/user/sagas/user.sagas.ts | 3 ++- src/modules/user/user.actions.ts | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/user/sagas/user.sagas.ts b/src/modules/user/sagas/user.sagas.ts index b02bfad..d2a0028 100644 --- a/src/modules/user/sagas/user.sagas.ts +++ b/src/modules/user/sagas/user.sagas.ts @@ -4,10 +4,11 @@ import { createUserFailed, createUserSucceeded, User, + CreateUserRequestedAction, } from '../user.actions'; import { userEditRequest } from '../user.api'; -export function* userEditSaga(action: any) { +export function* userEditSaga(action: CreateUserRequestedAction) { try { // define type explicitly because of lack of return types of generators: // https://github.com/redux-saga/redux-saga/issues/1286#issuecomment-482866473 diff --git a/src/modules/user/user.actions.ts b/src/modules/user/user.actions.ts index 0709383..d19f539 100644 --- a/src/modules/user/user.actions.ts +++ b/src/modules/user/user.actions.ts @@ -1,4 +1,4 @@ -import { createAction } from 'deox'; +import { createAction, Action } from 'deox'; export type User = { firstName: string; @@ -18,6 +18,12 @@ export const createUser = createAction( resolve => (user: User) => resolve(user), ); +export type CreateUserRequestedAction = Action< + ActionTypeKeys.CreateUserRequested +> & { + payload: User; +}; + export const createUserRequested = createAction( ActionTypeKeys.CreateUserRequested, resolve => (user: User) => resolve(user), From 2826fbd0f65213e94cba7dffb64cf702d53e2a3d Mon Sep 17 00:00:00 2001 From: Nico Peham Date: Mon, 29 Apr 2019 08:25:03 +0200 Subject: [PATCH 7/9] remove material ui icons and roboto font --- index.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/index.html b/index.html index aefb0b8..a2de2f3 100644 --- a/index.html +++ b/index.html @@ -5,14 +5,6 @@ - - Starter From f37bba809080a16f4a8a516b559e5b393554ef77 Mon Sep 17 00:00:00 2001 From: npeham Date: Mon, 29 Apr 2019 14:54:34 +0200 Subject: [PATCH 8/9] default import to import react --- src/setupEnzyme.ts | 2 +- tsconfig.json | 1 + tslint.json | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/setupEnzyme.ts b/src/setupEnzyme.ts index fbb8910..8c03ca5 100644 --- a/src/setupEnzyme.ts +++ b/src/setupEnzyme.ts @@ -1,3 +1,3 @@ import { configure } from 'enzyme'; -import * as EnzymeAdapter from 'enzyme-adapter-react-16'; +import EnzymeAdapter from 'enzyme-adapter-react-16'; configure({ adapter: new EnzymeAdapter() }); diff --git a/tsconfig.json b/tsconfig.json index a25079a..3e28f69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "skipLibCheck": true, "target": "es5", "strictNullChecks": true, + "esModuleInterop": true }, "include": ["./src/**/**/*"], "exclude": ["node_modules"] diff --git a/tslint.json b/tslint.json index c296ecb..0bf6d87 100644 --- a/tslint.json +++ b/tslint.json @@ -1,6 +1,9 @@ { "extends": ["tslint-config-airbnb", "tslint-react", "tslint-config-prettier"], "rules": { - "variable-name": [true, "allow-pascal-case"] + "variable-name": [true, "allow-pascal-case"], + "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks + "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies + "import-name": false } } From 7e7f492754bbba76d41074cddc0b1adfed212f4c Mon Sep 17 00:00:00 2001 From: npeham Date: Mon, 29 Apr 2019 16:18:17 +0200 Subject: [PATCH 9/9] sample hook implementation --- package.json | 1 + src/index.tsx | 5 +- src/modules/user/reducer/user.reducer.ts | 4 +- .../userEditForm/UserEditForm.container.tsx | 75 ++++++++++--------- src/modules/user/user.api.ts | 2 +- yarn.lock | 5 ++ 6 files changed, 53 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index b48d147..24cf2bc 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "react-router": "^4.3.1", "react-router-dom": "^4.3.1", "redux": "^4.0.1", + "redux-react-hook": "^3.3.1", "redux-saga": "^1.0.2" }, "devDependencies": { diff --git a/src/index.tsx b/src/index.tsx index 6581329..1391a06 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,17 +2,18 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; +import { StoreContext } from 'redux-react-hook'; declare let module: any; import { App } from './App'; import { store } from './configureStore'; const RenderApp = () => ( - + - + ); ReactDOM.render(, document.getElementById('root') as HTMLElement); diff --git a/src/modules/user/reducer/user.reducer.ts b/src/modules/user/reducer/user.reducer.ts index 50082bc..147e615 100644 --- a/src/modules/user/reducer/user.reducer.ts +++ b/src/modules/user/reducer/user.reducer.ts @@ -1,4 +1,4 @@ -import { User, createUser } from '../user.actions'; +import { User, createUser, createUserSucceeded } from '../user.actions'; import { createReducer } from 'deox'; export interface UserState { @@ -13,7 +13,7 @@ const userDefaultState: UserState = { }; export const userReducer = createReducer(userDefaultState, handle => [ - handle(createUser, (state, action) => { + handle(createUserSucceeded, (state, action) => { return { ...state, user: action.payload }; }), ]); diff --git a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx index ccdd162..63c4fe5 100644 --- a/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx +++ b/src/modules/user/scenes/userEdit/userEditForm/UserEditForm.container.tsx @@ -1,45 +1,52 @@ -import * as React from 'react'; -import { connect } from 'react-redux'; +// tslint:disable-next-line:import-name +import React, { useCallback } from 'react'; -import { - ConnectedReduxProps, - ApplicationState, -} from '../../../../../configureStore'; +import { ApplicationState } from '../../../../../configureStore'; import { UserEditForm } from './UserEditForm'; -import { User, createUser, createUserRequested } from '../../../user.actions'; +import { + User, + createUser, + createUserRequested, + createUserSucceeded, +} from '../../../user.actions'; +// tslint:disable-next-line:import-name +// import React, { useCallback } from 'react'; +import { useMappedState, useDispatch } from 'redux-react-hook'; + +// const { useCallback } = React; type UserEditFormContainerStateMapProps = { user: User; }; -const mapStateToProps = ( - state: ApplicationState, -): UserEditFormContainerStateMapProps => { - return { - user: state.userState.user, - }; +type RegisterFormContainerProps = { + passedProp: string; }; -type RegisterFormContainerProps = ConnectedReduxProps & - UserEditFormContainerStateMapProps & { - passedProp: string; +export const UserEditFormContainer: React.FunctionComponent< + RegisterFormContainerProps +> = ({ passedProp }) => { + const mapState = useCallback( + (state: ApplicationState): UserEditFormContainerStateMapProps => ({ + user: state.userState.user, + }), + [], + ); + const dispatch = useDispatch(); + + const { user } = useMappedState(mapState); + + const handleRegisterFormSubmit = (values: User) => { + dispatch(createUserRequested(user)); }; -export const UserEditFormContainer = connect(mapStateToProps)( - ({ dispatch, user, passedProp }: RegisterFormContainerProps) => { - const handleRegisterFormSubmit = (values: User) => { - // access to values - dispatch(createUserRequested(values)); - }; - - return ( - <> -
passed: {passedProp}
-
user in state: {JSON.stringify(user)}
-
-
- {' '} - - ); - }, -); + return ( + <> +
passed: {passedProp}
+
user in state: {JSON.stringify(user)}
+
+
+ {' '} + + ); +}; diff --git a/src/modules/user/user.api.ts b/src/modules/user/user.api.ts index 4a14f95..fa647c7 100644 --- a/src/modules/user/user.api.ts +++ b/src/modules/user/user.api.ts @@ -2,5 +2,5 @@ import { User } from './user.actions'; export const userEditRequest = async (user: User): Promise => { const fetchedUser: User = { firstName: 'ie', lastName: 'sei' }; - return fetchedUser; + return new Promise(resolve => setTimeout(() => resolve(fetchedUser), 1000)); }; diff --git a/yarn.lock b/yarn.lock index feba418..a034245 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5459,6 +5459,11 @@ redux-immutable-state-invariant@^2.1.0: invariant "^2.1.0" json-stringify-safe "^5.0.1" +redux-react-hook@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/redux-react-hook/-/redux-react-hook-3.3.1.tgz#3fc91c0def8a49b873c92879843b3f76c8ae7cf4" + integrity sha512-a5RHGYT2ZV8Zo1ATP2ubxQGm0fPwn62loSieTeCL7PRgGfdTi8jR0S1K+1/1b4P6RSnEXvs5qC9IVlh2clPbxg== + redux-saga@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.0.2.tgz#0599703f975438f1b2f9d2b9a965ec58f0fdfcd7"