Skip to content

PatrykRudzinski/React_CSR_init

Repository files navigation

React CSR boilerplate

  1. Configuration
  2. Files structure
  3. Recommendations
  4. Good to know

Configuration

Requirements

To run it locally you need node and yarn installed or just use Docker.

Node


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

Yarn


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

Available scripts

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

⬆ back to top

Files structure

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.

Source files

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

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.

/pages

Containers for views, that directory should a mirror of routing system in general.

/components & /containers

Place for components and containers:

components atomic reusable components

containers mainly wrappers for components

/hooks

Any kind of custom hooks.

/context

Place for Context logic. It would be nice if all context files will be created in one convention.

/config

Put here config files for used libraries.

/styles

Everything related to styles, themes or animations.

/utils

Place for widely used functions which aren't components, but can be used anywhere.

/assets

Keep here images, videos etc.

Special files

.babelrc

Allow to pass configuration to babel.

.env.*

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

config-overrides.js

Here you can override webpack configuration without ejecting. Look here.

jsconfig.json

Set src as base url, that allows to import directly from /src, for example: import client from 'config/apollo/client';

Utils

In /scripts you can find useful utilities for project management:

Generate Component

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

⬆ back to top

Recommendations

HTML

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.

CSS

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.

JavaScript

General

Destructuring

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> )
Spread

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>
);
Optional chaining

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 occur TypeError: Cannot read property 'general' of undefined,
  • If we try to access to field title of posts[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;

⬆ back to top

React

PropTypes

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.

Docs

Generic components

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

One component per file

Do not declare more than one component in file (styled components are exception). That rule helps to keep application in React way.

Function as a first choice

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.

Keep components as simple as it possible

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.

⬆ back to top

Good to know

I highly recommend one chapter form react docs: Thinking in React, but rest of it is very valuable too.

⬆ back to top

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published