Skip to content
Draft
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 0 additions & 8 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<base href="/" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<title>Starter</title>
</head>
<body>
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,23 @@
]
},
"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",
"redux-react-hook": "^3.3.1",
"redux-saga": "^1.0.2"
},
"devDependencies": {
"@types/enzyme": "^3.9.0",
"@types/enzyme-adapter-react-16": "^1.0.5",
"@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",
Expand Down
20 changes: 20 additions & 0 deletions src/configureStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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;
}

export interface ApplicationState {
userState: UserState;
}

const sagaMiddleware = createSagaMiddleware();

export const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(rootSaga);
12 changes: 8 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
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 = () => (
<BrowserRouter>
<App />
</BrowserRouter>
<StoreContext.Provider value={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</StoreContext.Provider>
);

ReactDOM.render(<RenderApp />, document.getElementById('root') as HTMLElement);
Expand Down
19 changes: 19 additions & 0 deletions src/modules/user/reducer/user.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { User, createUser, createUserSucceeded } 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(createUserSucceeded, (state, action) => {
return { ...state, user: action.payload };
}),
]);
Empty file.
27 changes: 27 additions & 0 deletions src/modules/user/sagas/user.sagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { takeLatest, call, put } from 'redux-saga/effects';
import {
ActionTypeKeys,
createUserFailed,
createUserSucceeded,
User,
CreateUserRequestedAction,
} from '../user.actions';
import { userEditRequest } from '../user.api';

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
const fetchedUser: User = yield call(userEditRequest, {
firstName: 'nico',
lastName: 'peham',
});
yield put(createUserSucceeded(fetchedUser));
} catch (error) {
yield put(createUserFailed());
}
}

export const userSagas = [
takeLatest(ActionTypeKeys.CreateUserRequested, userEditSaga),
];
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +17,9 @@ export const UserEditSene: React.SFC<
<span>userId --> {userId}</span>
<br />
<span>projId --> {projId}</span>
<br />
<br />
<UserEditFormContainer passedProp="myPassedProp" />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// tslint:disable-next-line:import-name
import React, { useCallback } from 'react';

import { ApplicationState } from '../../../../../configureStore';
import { UserEditForm } from './UserEditForm';
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;
};

type RegisterFormContainerProps = {
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));
};

return (
<>
<div>passed: {passedProp}</div>
<div>user in state: {JSON.stringify(user)}</div>
<br />
<br />
<UserEditForm onSubmit={handleRegisterFormSubmit} />{' '}
</>
);
};
20 changes: 20 additions & 0 deletions src/modules/user/scenes/userEdit/userEditForm/UserEditForm.tsx
Original file line number Diff line number Diff line change
@@ -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<UserEditFormProps> = ({ onSubmit }) => {
const userEdited: User = {
firstName: 'susan',
lastName: 'jackson',
};

const onUserEdited = () => onSubmit(userEdited);
return (
<>
<button onClick={onUserEdited}>update state</button>
</>
);
};
37 changes: 37 additions & 0 deletions src/modules/user/user.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createAction, Action } from 'deox';

export type User = {
firstName: string;
lastName: string;
};

// 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(
ActionTypeKeys.CreateUser,
resolve => (user: User) => resolve(user),
);

export type CreateUserRequestedAction = Action<
ActionTypeKeys.CreateUserRequested
> & {
payload: 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);
6 changes: 6 additions & 0 deletions src/modules/user/user.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { User } from './user.actions';

export const userEditRequest = async (user: User): Promise<User> => {
const fetchedUser: User = { firstName: 'ie', lastName: 'sei' };
return new Promise(resolve => setTimeout(() => resolve(fetchedUser), 1000));
};
5 changes: 4 additions & 1 deletion src/modules/user/user.routing.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/rootReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { combineReducers } from 'redux';
import { userReducer } from './modules/user/reducer/user.reducer';

export const rootReducer = combineReducers({ userState: userReducer });
7 changes: 7 additions & 0 deletions src/rootSaga.ts
Original file line number Diff line number Diff line change
@@ -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]);
}
2 changes: 1 addition & 1 deletion src/setupEnzyme.ts
Original file line number Diff line number Diff line change
@@ -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() });
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"skipLibCheck": true,
"target": "es5",
"strictNullChecks": true,
"esModuleInterop": true
},
"include": ["./src/**/**/*"],
"exclude": ["node_modules"]
Expand Down
5 changes: 4 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading