To run it locally you need node
and yarn
installed or just use Docker.
Windows:
Download installer from here
Linux (Ubuntu):
Run:
sudo apt-get update
sudo apt-get install nodejs
To manage node version easily use n from npm:
sudo yarn cache clean
sudo yarn install -g n
sudo n <version>
version => any node version (can be stable or latest too)
Other:
Check it
Windows
Just download installer from here
Note: Remember to install node before yarn!
Linux (Ubuntu)
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
Other
Check it
yarn run start
It runs development server on localhost:3000
yarn run build
Prepare production build in /build
yarn run test
It runs tests
yarn run analyze
Tool for analyze bundle size
yarn run eject
Note: this is a one-way operation. Once you eject
, you can’t go back!
That gives you full access to webpack configuration, but thanks to custoimze-cra
you won't need it
yarn run lint
Audit your code with ESLint
yarn run lint:fix
As above, but will fix some errors automatically
That's my proposal files structure in React project. Feel free to adjust that to current project, but please be consistent in your choices. Keep in mind, is very easy to make React project bloated, so try hard to keep files arrangement in any logic way.
My general approach is create directory for every component, and export it as default. So it should looks something like that:
Button/ | ||
---|---|---|
_components/ | optional directory, use it if main component needs any dedicated child component, but don't go recursive | |
_utils/ | optional directory, keep here any separated logic used by main components | |
Button.jsx | component file | |
Button.test.jsx | test file | |
index.js | just exports Button.jsx as default, to import it in easy way |
Some directories has global index.js. That allows perform a multiple import, for example: import { Button, PageTitle } from 'components'
. But be careful to don't create a dependency cycle.
To avoid creating all that structure manually check generateComponent in Utils.
Note: to prevent PyCharm Module is not installed
warning on imported files just mark src as Sources Root.
All application logic is placed in /src
:
Root of application:
App.jsx
is a good place to wrap application with any kind of global providers, or import application-wide files.
Router.jsx
define global router here, but keep in mind use object as router definition can be better idea than hardcode it in jsx. As probably you already know nesting is possible in react-router, if you need create it in specific view.
Containers for views, that directory should a mirror of routing system in general.
Place for components and containers:
components
atomic reusable components
containers
mainly wrappers for components
Any kind of custom hooks.
Place for Context logic. It would be nice if all context files will be created in one convention.
Put here config files for used libraries.
Everything related to styles, themes or animations.
Place for widely used functions which aren't components, but can be used anywhere.
Keep here images, videos etc.
Allow to pass configuration to babel.
Place to keep environment variables
Each variable must start with REACT_APP_
, later you can access to it by process.env.REACT_APP_*
, but NODE_ENV
is still accessible.
Wildcard in .env file can be replaced by: local, development, test, production, development.local, test.local or production.local.
Warning: Do not keep any secrets here!
Priority:
comment | 1 | 2 | 3 | 4 |
---|---|---|---|---|
yarn start | .env.development.local | .env.development | .env.local | .env |
yarn build | .env.production.local | .env.production | .env.local | .env |
yarn test | .env.test.local | .env.test | .env |
Here you can override webpack configuration without ejecting. Look here.
Set src as base url, that allows to import directly from /src
, for example: import client from 'config/apollo/client';
In /scripts
you can find useful utilities for project management:
node ./scripts/generators/generateComponent
After providing ComponentName
and path
creates following files structure in directory:
| ComponentName
|- - ComponentName.jsx (with base React component template)
|- - index.js (default export)
To update global index.js just run it with flag -u
, leaving empty name will cause just global index.js update without creating component.
You can pass COMPONENT_PATH variable to avoid asking for path
Finally React always is rendered to HTML, so please keep that in mind and use semantic tags in proper way. Do not limit yourself to div
and img
.
Main goal is dont repeat yourself. I highly recommend styled-components to implement styles.
Most of css frameworks can handle theme provider logic, so use it as well. I've created _core.js
file which contains core variables and functions (take a moment to analyze that file please), and specific themes like default or dark. Themes extends core object and add extra variables, that allow us to switch between themes (analyze src/context/ThemeContext
and src/components/ThemeSwitcher
).
As you can find in /styles
I've created animation directory to keep all animations in one place.
Destructuring allows you to access function parameters in place where you declare it. It's very handy in react components:
const Button = ({ color, children }) => (
<button style={{ color }}>
{ children }
</button>
)
instead of:
const Button = props => (
<button style={{ color: props.color }}>
{ props.children }
</button>
)
Note #1: in case of variable name conflicts you can destructure it with name change:
const { color: colorFromProps } = props;
Note #2: ES6 allows us to use object property shorthand, as you could notice in first example, if object property name is the same as value name we can:
const style = {
color,
}
instead of:
const style = {
color: color,
}
Note #3: Arrays can be destructured too
Best example, useState
hook:
const [ color, setColor ] = useState(null);
// useState functions returns array of two elements
// useState()[0] = state value
// useState()[1] = function to update state (recommended naming: set<value> )
That allow us to spread iterable types. There are some use cases:
Array or object copy:
const obj = { a: 1 };
const objCp = { ...obj };
objCp.a = 2;
// obj.a => 1
// objCp.a => 2
Concatenate Arrays or Objects:
const ar1 = [1, 2, 3];
const ar2 = [4, 5, 6];
const ar = [...ar1, ...ar2];
// ar => [1, 2, 3, 4, 5, 6]
Extending object:
const coreObj = {
a: 1,
b: {
c: 2,
},
};
const extObj = {
...coreObj,
b: {
...coreObj.b,
d: 3,
}
};
Passing additional props:
const Button = ({ children, ...rest }) => (
<button { ...rest }>
{ children }
</button>
);
It's a proposal syntax for JS, but as long as we use babel and @babel/plugin-proposal-optional-chaining
we can use it in safe way. Optional chaining resolves problem of far nested objects returned from graphQL queries.
Let's consider that case:
Structure of object returned by API:
{
general: {
posts: [
{
tile: 'String',
},
]
}
}
- If we try to access to field
general
in object (let's name it data) until we get response Type error will occurTypeError: Cannot read property 'general' of undefined
, - If we try to access to field
title
ofposts[0]
after response, but for any reason array will be empty the same error will occur.
So we need check every uncertain nodes:
const title = data && data.general.posts[0] && data.general.posts[0].title
Using optional chaining we can perform that in more clear way, just delegating undefined
:
const title = data?.general.posts[0]?.title;
As you know JS is dynamically typed language, what can cause problems in some cases, especially in React. So it's good practice to use PropTypes in proper way. Just use it to define all props types to avoid issues or detect it quickly.
Create as much generic components as it is possible, for example instead of creating <ButtonPrimary />
and <ButtonSecondary />
just create <Button displayType='primary'/>
and implement logic to manage display type basing on props
Do not declare more than one component in file (styled components are exception). That rule helps to keep application in React way.
Hooks create possibility to almost completely abandon class component, so let's use it and don't create class components if we can achieve the same effect with function.
Try to keep components as simple as you can. Don't create monolithic component with tons of logic, just find way to split it to atomic components and basing on props passing or context API create any smart data flow.
I highly recommend one chapter form react docs: Thinking in React, but rest of it is very valuable too.