Skip to content

Commit

Permalink
Update redux structure to use redux-toolkit.
Browse files Browse the repository at this point in the history
  • Loading branch information
sachie committed Dec 11, 2021
1 parent 6353e8f commit f6d71a2
Show file tree
Hide file tree
Showing 16 changed files with 147 additions and 63 deletions.
4 changes: 4 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
"max-len": [2, { "code": 100 }],
"consistent-return": 0,
"default-param-last": 0,
"no-param-reassign": [2, {
"props": true,
"ignorePropertyModificationsFor": ["state"]
}],
"import/order": [2, {
"groups": [
"builtin",
Expand Down
File renamed without changes.
19 changes: 19 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

Fixes # (issue)

## Type of change

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change

# Checklist:

- [ ] Tests updated
- [ ] Documentation updated
- [ ] Code comments included
- [ ] Lint passing
- [ ] Tests passing
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</div>
<br />

A modern template to jump start your React project. The usual additions you make after CRA generation have already been done for you, including code styling rules, auto formatting, test setup, coverage reports, SCSS modules, redux setup and more (check the feature list below for more information). You can simply clone-build-run and start adding features, without spending time on setting up folder structures, linting, git hooks, etc.
A modern template to jump start your React project. The usual additions you make after CRA generation have already been done for you, including code styling rules, auto formatting, test setup, coverage reports, SCSS modules, modern redux setup and more (check the feature list below for more information). You can simply clone-build-run and start adding features, without spending time on setting up folder structures, linting, git hooks, etc.

Built on top of the latest create-react-app, as of Nov 2021, with the following module versions:

Expand Down Expand Up @@ -56,7 +56,7 @@ Check the code comments for tips and explanations.

- **_Absolute imports_** to prevent loads of `../../` import statements.

- **_Redux structure_** with a minimal store setup and connection. (Can be removed easily if it's not needed)
- **_Modern Redux structure_** using [Redux Toolkit](https://redux-toolkit.js.org/) which is [recommended by Redux](https://redux.js.org/introduction/getting-started#redux-toolkit). (Can be removed easily if it's not needed)

- **_Test setup_** with examples, lint configs and scripts for coverage reporting (with [istanbul](https://github.com/gotwarlost/istanbul)), and CI support.

Expand All @@ -68,7 +68,7 @@ Check the code comments for tips and explanations.

## What's not included

- No comprehensive redux examples, such as component specific reducers, etc. As different developers have different preferences for this structure, and some might opt out of redux completely.
- No examples of redux middleware or enhancers, as developer preferences vary for them.

- No server side rendering setup.

Expand Down Expand Up @@ -106,7 +106,7 @@ The following scripts are also included if the linters need to be used separatel
## Discussions and Contributions

For any questions, suggestions or comments, please use the github discussions section.
If you have an improvement to submit, feel free to open a PR.
If you have an improvement to submit, feel free to open a PR. For more details, check the [Contribution guidelines](https://github.com/sachie/react-quickstart/blob/main/CONTRIBUTING.md)

&nbsp;

Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"eslint:fix": "eslint --fix .",
"stylelint": "stylelint \"**/*.{css,scss,sass,js}\"",
"stylelint:fix": "stylelint --fix \"**/*.{css,scss,sass,js}\"",
"prettier": "prettier --check './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc",
"prettier:fix": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc",
"prettier": "prettier --check './**/*.{js,jsx,ts,tsx,css,json}' --config ./.prettierrc",
"prettier:fix": "prettier --write './**/*.{js,jsx,ts,tsx,css,json}' --config ./.prettierrc",
"postinstall": "husky install"
},
"eslintConfig": {
Expand All @@ -39,21 +39,22 @@
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!src/index.js"
"!src/index.js",
"!src/store.js"
]
},
"dependencies": {
"@reduxjs/toolkit": "^1.7.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.0",
"@testing-library/user-event": "^13.2.0",
"classnames": "^2.2.6",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.5",
"react-redux": "^7.2.6",
"react-router-dom": "^6.0.2",
"react-scripts": "^4.0.3",
"redux": "^4.0.5"
"react-scripts": "^4.0.3"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
Expand All @@ -73,7 +74,6 @@
"husky": "^7.0.1",
"istanbul": "^0.4.5",
"prettier": "^2.4.0",
"redux-devtools-extension": "^2.13.8",
"sass": "^1.44.0",
"sass-loader": "^12.1.0",
"stylelint": "^13.13.1",
Expand Down
15 changes: 8 additions & 7 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import messages from 'utils/messages';
import App from './App';
import store from './store';

test('renders title and readme link', () => {
render(
<MemoryRouter>
<App />
</MemoryRouter>,
<Provider store={store}>
<MemoryRouter>
<App />
</MemoryRouter>
</Provider>,
);
const titleElement = screen.getByText(messages.common.title);
expect(titleElement).toBeInTheDocument();

const linkElement = screen.getByText(messages.mainPage.readme);
expect(linkElement).toBeInTheDocument();
expect(screen.getByText(messages.common.title)).toBeInTheDocument();
});
4 changes: 2 additions & 2 deletions src/components/ReactLogo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { memo } from 'react';
import Logo from 'assets/icons/logo.svg';
import styles from './reactLogo.module.scss';

const ReactLogo = () => {
return <img className={styles.logo} src={Logo} alt="Logo" />;
const ReactLogo = ({ ...props }) => {
return <img className={styles.logo} src={Logo} alt="Logo" {...props} />;
};

export default memo(ReactLogo);
10 changes: 1 addition & 9 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import { createStore, compose } from 'redux';
import App from 'App';
import rootReducer from 'reducers';
import store from './store';

import './assets/styles/index.scss';

const composeEnhancers =
typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;

const store = createStore(rootReducer, composeEnhancers());

ReactDOM.render(
<Provider store={store}>
<Router>
Expand Down
19 changes: 19 additions & 0 deletions src/pages/Home/homeSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
value: 0,
};

export const homeSlice = createSlice({
name: 'home',
initialState,
reducers: {
increment: state => {
state.value += 1;
},
},
});

export const { increment } = homeSlice.actions;

export default homeSlice.reducer;
10 changes: 9 additions & 1 deletion src/pages/Home/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { memo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import cx from 'classnames';
import ReactLogo from 'components/ReactLogo';
import constants from 'utils/constants';
import messages from 'utils/messages';
import { increment } from './homeSlice';
import pageStyles from 'pages/pages.module.scss';
import styles from './home.module.scss';

const Home = () => {
const count = useSelector(({ home }) => home.value);
const dispatch = useDispatch();

return (
<div className={cx(pageStyles.container, styles.main) /* Example of class combining */}>
<ReactLogo />
<ReactLogo onClick={() => dispatch(increment())} />
<h2>{messages.common.title}</h2>
<p>{messages.mainPage.getStarted}</p>
<p>
Expand All @@ -19,6 +24,9 @@ const Home = () => {
</a>
{messages.mainPage.moreInfo}
</p>
<div data-testid="count" hidden>
{count}
</div>
</div>
);
};
Expand Down
28 changes: 28 additions & 0 deletions src/pages/Home/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import store from 'store';
import messages from 'utils/messages';
import Home from './index';

test('renders title and readme link', () => {
render(
<Provider store={store}>
<Home />
</Provider>,
);

expect(screen.getByText(messages.common.title)).toBeInTheDocument();
expect(screen.getByText(messages.mainPage.getStarted)).toBeInTheDocument();
expect(screen.getByText(messages.mainPage.visitThe, { exact: false })).toBeInTheDocument();
expect(screen.getByText(messages.mainPage.readme)).toBeInTheDocument();
expect(screen.getByText(messages.mainPage.moreInfo, { exact: false })).toBeInTheDocument();
expect(screen.getByText(messages.mainPage.moreInfo, { exact: false })).toBeInTheDocument();

const countElement = screen.getByTestId('count');
const logoElement = screen.getByAltText('Logo');
expect(countElement).toHaveTextContent('0');
logoElement.click();
expect(countElement).toHaveTextContent('1');
logoElement.click();
expect(countElement).toHaveTextContent('2');
});
12 changes: 0 additions & 12 deletions src/pages/Home/reducer.js

This file was deleted.

22 changes: 11 additions & 11 deletions src/pages/Home/reducer.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import reducer from './reducer';
import reducer, { increment } from './homeSlice';

describe('home reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual({});
});
const initialState = {
value: 0,
};

test('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(initialState);
});

it('should handle a test action', () => {
expect(
reducer([], {
type: 'home/some/action',
}),
).toEqual({});
test('should handle an increment', () => {
expect(reducer(initialState, increment())).toEqual({
value: 1,
});
});
4 changes: 0 additions & 4 deletions src/reducers/index.js

This file was deleted.

9 changes: 9 additions & 0 deletions src/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { configureStore } from '@reduxjs/toolkit';
import homeReducer from 'pages/Home/homeSlice';

export default configureStore({
reducer: {
home: homeReducer,
},
devTools: process.env.NODE_ENV !== 'production',
});
32 changes: 26 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,16 @@
schema-utils "^2.6.5"
source-map "^0.7.3"

"@reduxjs/toolkit@^1.7.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.7.0.tgz#fe00b8869bc076cf92efc00417832822d1e5c367"
integrity sha512-iApo4zS+8kWnIn4xucTDWpqRjDNkXruFIyJQWwThIEIbMj5kwqvbMaQcEgd2a305B68Z+4bvZqAqJSATeddaJA==
dependencies:
immer "^9.0.7"
redux "^4.1.2"
redux-thunk "^2.4.1"
reselect "^4.1.5"

"@rollup/plugin-node-resolve@^7.1.1":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca"
Expand Down Expand Up @@ -6283,6 +6293,11 @@ immer@8.0.1:
resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656"
integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==

immer@^9.0.7:
version "9.0.7"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.7.tgz#b6156bd7db55db7abc73fd2fdadf4e579a701075"
integrity sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==

immutable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
Expand Down Expand Up @@ -10014,7 +10029,7 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==

react-redux@^7.2.5:
react-redux@^7.2.6:
version "7.2.6"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"
integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==
Expand Down Expand Up @@ -10212,12 +10227,12 @@ redent@^3.0.0:
indent-string "^4.0.0"
strip-indent "^3.0.0"

redux-devtools-extension@^2.13.8:
version "2.13.9"
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7"
integrity sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==
redux-thunk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==

redux@^4.0.0, redux@^4.0.5:
redux@^4.0.0, redux@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
Expand Down Expand Up @@ -10387,6 +10402,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=

reselect@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==

resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
Expand Down

0 comments on commit f6d71a2

Please sign in to comment.