Skip to content
This repository was archived by the owner on Apr 30, 2023. It is now read-only.

Add base template#1

Merged
markerikson merged 28 commits intoreduxjs:masterfrom
BenLorantfy:master
Feb 17, 2020
Merged

Add base template#1
markerikson merged 28 commits intoreduxjs:masterfrom
BenLorantfy:master

Conversation

@BenLorantfy
Copy link
Copy Markdown
Contributor

@BenLorantfy BenLorantfy commented Dec 10, 2019

Creates a base template using @reduxjs/toolkit.

Started with cra default template and made some changes:

  • Added @reduxjs/toolkit, and react-redux as dependencies
  • Added "ducks" folder with one duck for a counter state
  • Added store.js that creates the store with the counter slice
  • Changed index.js to wrap App with Provider
  • Added a counter UI to demonstrate dispatching and selecting from the counter slice
  • Changed react branding to redux branding
  • Setup linting/prettier/testing for internal development (not published with template)

A few open questions I have:

  • Any best practices I'm missing?
  • Are the exports for the counter slice are done properly?
  • Is there any other opinions we want to bake into the template?
  • Do we want the "Learn Redux" link to link to redux.js.org or redux-toolkit.js.org ?

Screenshot of generated app:
Screen Shot 2020-02-17 at 4 19 15 PM

@markerikson
Copy link
Copy Markdown
Contributor

First observation based on the description: I'd prefer to have the folder named features instead of ducks.

Also, I'd like to consider moving App.js and store.js into a src/app folder as well, similar to these examples:

https://github.com/reduxjs/rtk-github-issues-example

https://redux.js.org/style-guide/style-guide#structure-files-as-feature-folders-or-ducks

@nickserv
Copy link
Copy Markdown
Contributor

nickserv commented Dec 10, 2019

@markerikson I think moving App.js to a directory other than src could be confusing migrating from the default CRA template, but other than that I like using features and being consistent with the docs/examples

@nickserv

This comment has been minimized.

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

@nickmccurdy That's probably a good idea

@nickserv nickserv mentioned this pull request Dec 12, 2019
@nickserv
Copy link
Copy Markdown
Contributor

nickserv commented Dec 12, 2019

@BenLorantfy I've created a PR (#2). If you'd like to add me to your fork, I can help you merge the default template into it.

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

No worries, I can handle rebasing it after #2 get's merged 🙂

@nickserv
Copy link
Copy Markdown
Contributor

I’m working on a code review, I’m not sure if rebasing preserves that

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

ah ok I can merge instead then

@nickserv
Copy link
Copy Markdown
Contributor

nickserv commented Dec 12, 2019

Do we want the "Learn Redux" link to link to redux.js.org or redux-toolkit.js.org?

I'd like to link to Redux, Redux Toolkit, and React Redux in that order. Note that Redux Toolkit doesn't provide React bindings, and the docs don't seem to provide an introduction to Redux for new users.

Copy link
Copy Markdown
Contributor

@nickserv nickserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your help! Here are my thoughts so far.

Comment thread template.json Outdated
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/user-event": "^7.1.2",
"react-redux": "^7.1.3",
"@reduxjs/toolkit": "^1.1.0"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reduxjs/toolkit should be first to keep consistent order with npm install, so installing new packages doesn't create a noisier diff.

Comment thread template/README.md Outdated
@@ -0,0 +1,68 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should link to the Redux and/or Toolkit docs at the top. Should still link to the create-react-app docs and keep the rest of the readme though, as it's still relevant.

Comment thread template/src/App.css Outdated
.App-counter {
display: flex;
align-items: center;
/* margin-top: 14px; */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should either uncomment or remove this.

Comment thread template/src/ducks/counter.js Outdated

const { actions } = slice;

export { actions, selectors, slice };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I find separate export lines easier to read.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer simplicity and convention in a template.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Should I be exporting an object of selectors or individually exporting each selector? Same for actions?

Comment thread package.json Outdated
"name": "cra-template-redux",
"version": "1.0.0",
"scripts": {
"prettify": "prettier './template/src/**/*.js' --write",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should use CRA's Prettier config so there's less noise if someone switches from the default template.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't have a config file but I added the same flags they use here: https://github.com/facebook/create-react-app/blob/master/package.json#L19

Comment thread template/src/store.js Outdated
}
});

export default store;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have export default configureStore(...).

Comment thread template/src/App.test.js
);
const linkElement = getByText(/learn redux/i);
expect(linkElement).toBeInTheDocument();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a good opportunity to show an example of testing a simple Redux app, since it's more interactive than the default template. If we're just sticking with a basic smoke test this is fine with me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markerikson any thoughts on this? I can do a more in-depth test if we want.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

@leocaseiro leocaseiro Jan 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a suggestion:

<span data-testid="count-value" className="App-counter-value">
	{count}
</span>
const setup = () =>
	render(
		<Provider store={store}>
			<App />
		</Provider>
	);

test('renders learn redux toolkit link', () => {
	const { getByText } = setup();

	const linkElement = getByText(/redux toolkit/i);

	expect(linkElement).toBeInTheDocument();
});

test('can increment value', () => {
	const { getByTestId, getByText } = setup();
	fireEvent.click(getByText('+'));
	expect(getByTestId('count-value')).toHaveTextContent('1');
});

test('can decrement value', () => {
	const { getByTestId, getByText } = setup();
	fireEvent.click(getByText('-'));
	expect(getByTestId('count-value')).toHaveTextContent('0');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also believe it's worth to add tests for the reducer as well:

// src/features/counter.test.js
import counter, { actions } from './counter';

test('can increment value', () => {
	expect(
		counter(
			{ value: 1 },
			{
				type: actions.increment.type,
			}
		).value
	).toEqual(2);
});

test('can decrement value', () => {
	expect(
		counter(
			{ value: 2 },
			{
				type: actions.decrement.type,
			}
		).value
	).toEqual(1);
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add something like that if we decide we want to add more in-depth testing.

@luismasg
Copy link
Copy Markdown

could we add a prepare function somewhere?
And a thunk ?
I could whip up a jsonplaceholder -ish PR
could this be a good opportuniy to recommend how to handle NOT_STARTED , LOADING and ERROR states in an action ?

@nickserv
Copy link
Copy Markdown
Contributor

nickserv commented Dec 20, 2019

Now that the default template (#2) is merged, we can merge master back into this branch.

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

First observation based on the description: I'd prefer to have the folder named features instead of ducks.

Should I also move the counter UI to the counter feature folder? (and turn counter.js into a folder?)

Comment thread template/src/App.test.js
</Provider>
);

expect(getByText(/learn/i)).toBeInTheDocument();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe search for: /react redux/i instead of just /learn/i?

expect(getByText(/react redux/i)).toBeInTheDocument();

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

@markerikson @nickmccurdy FYI, this is ready for another review 🙂

Comment thread package.json
"template.json"
]
],
"devDependencies": {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where in the template process are these devDeps actually used? Why isn't most of this already handled as part of CRA / react-scripts itself?

Copy link
Copy Markdown
Contributor Author

@BenLorantfy BenLorantfy Jan 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These dependencies don't end up in the project created by cra. These are just used by us to lint and test the code during development.

Comment thread template/src/App.css Outdated
align-items: center;
}

.App-counter-value {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this UI-specific CSS should be moved into a CSS modules file.

Comment thread template/src/App.js Outdated
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p className="App-counter">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move all the counter UI into a <Counter /> component inside of features/counter.

Comment thread template/src/features/counter.js Outdated
@@ -0,0 +1,25 @@
import { createSlice } from '@reduxjs/toolkit';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like this file to be moved to features/counter/counterSlice.js.

Comment thread template/src/features/counter.js Outdated

const selectCount = state => state.counter.value;

export const selectors = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think exporting the selector inside of an object helps here. Just do export const selectCount =.

For that matter, does the selector really buy us anything useful in terms of the example? It at least shows the idea of a selector, but it also doesn't show use of Reselect.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always like using selectors even for small things, since it hides the data structure from the consumers and therefore is easier to change. IMO the data structure of the state should be an implementation detail of the slice.

Up to you though.

Comment thread template/src/features/counter.js Outdated
selectCount,
};

export const { actions } = slice;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Export the action creators individually here:

Suggested change
export const { actions } = slice;
export const { increment, decrement} = slice.actions;

Comment thread template/src/store.js Outdated
@@ -0,0 +1,8 @@
import { configureStore } from '@reduxjs/toolkit';
import * as slices from './features';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the import * here. I'd rather explicitly do import counterReducer from "features/counter/counterSlice".

Similarly, I'd rather not bother with trying to reuse the slice.name field. Just say counter: counterReducer.

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

@markerikson @nickmccurdy Sorry for the delay; this is ready for another review now.

@@ -0,0 +1,31 @@
.container {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of naming convention, I've always preferred to make my CSS module files exactly match the name of the corresponding component files. In this case, that'd be capitalizing it to Counter.module.css.

},
reducers: {
increment: state => {
state.value += 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth adding an explanatory comment here saying that "Redux Toolkit allows us to 'mutate' the state"?

increment: state => {
state.value += 1;
},
decrement: state => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see one other case handled here that actually shows using the action parameter as well. Maybe have an incrementByAmount case, and allow the user to enter a number in the UI?

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

@markerikson This is ready for another review 🙂

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

@markerikson Just pinging

Comment thread template/src/App.js
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we leave the "Learn React" link in here? That's over half of what the final app will be anyway :)

@@ -0,0 +1,42 @@
import React, { useState } from 'react';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, one other naming scheme thing.

I'd like to change this to Counter.js.

I've never been a fan of the "using index.js for all the components based on folders" thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. Should the folder name be counter or Counter ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

features/counter/Counter.js, please

@markerikson
Copy link
Copy Markdown
Contributor

Okay, I think I'm down to two nitpicks at this point.

@markerikson
Copy link
Copy Markdown
Contributor

Awright, this is probably good enough to go with for now. Let's get this in!

Uh... what are the next steps? I assume we need to re-publish the package. I have access to the cra-template-redux package on NPM, so I guess it's up to me to do that?

@markerikson markerikson merged commit ff00b3d into reduxjs:master Feb 17, 2020
@BenLorantfy
Copy link
Copy Markdown
Contributor Author

Sounds good to me 👍

@markerikson
Copy link
Copy Markdown
Contributor

Awright, I'll try publishing it this evening.

Would we want to call this 1.0.0?

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

I'd say so

@nickserv
Copy link
Copy Markdown
Contributor

Let me know if you need help with docs or any other release stuff

@markerikson
Copy link
Copy Markdown
Contributor

I published 0.0.1, and did find one bug: the CSS module import was now mismatched. Fixed that, published 0.0.2, created another CRA project with it, and that worked. Soooo... I've just published 1.0.0 !

https://github.com/reduxjs/cra-template-redux/releases/tag/v1.0.0

@markerikson
Copy link
Copy Markdown
Contributor

The next step would be to tackle https://github.com/reduxjs/cra-template-redux-typescript . We should be able to piece it together by starting from the CRA-TS base template, copying over the code contents from this template, and TS-ifying it.

@BenLorantfy , you want to tackle that one too?

@BenLorantfy
Copy link
Copy Markdown
Contributor Author

Sure I can take a shot at it

@nickserv
Copy link
Copy Markdown
Contributor

Just read through the template again and I think it's a solid overview of basic CRA and Redux Toolkit functionality, nice work!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants