This document serves as a guide for using and implementing React.JS within our codebase.
- Prerequisites
- Styling
- File Organization
- State Management
- Functional Components and Hooks
- Splitting Components
- Testing
- JSX
- PropTypes - Type Checking
- Best Practices
- Conclusion
Developers with some JavaScript / React knowledge will familiarize very much easier.
- React.JS documentation: Official React.JS Documentation
-
- Complete the starting tic tac toe challenge
- Thinking in React: React Documentation
-
- Step 1 to be understand but check other steps as well
- ES6 Features: ES6 Guide
-
- Get familiar with modern JavaScript Concepts
- Sass Documentation: Official Sass Guide
-
- Familiarize yourself with mixins
-
- Familiarize yourself with variables
-
- Familiarize with .module files
- The Chase: The Chase - H5P Content Type
-
- Could be checked for some examples
- JavaScript Documentation: Mozilla Developer Network
- Webpack bundler: Webpack guide
jsconfig.json
Information: VSC Documentation about jsconfig
We use Sass modules for styling our components. Each component should have its own Sass file located in the styles
directory, mirroring the structure of the components
directory.
Our file tree should look closely to:
src
|- assets
|- components
|- context
|- helpers
|- hooks
|- styles
|- utils
The folders and files inside components
and styles
follow the same structure.
Each component directory should have an index.js
file to export the component. This approach simplifies imports elsewhere in the application. No need to write the full path of a component to import it.
[!example]
index.js
structure// One component to export from the folder import Component from './Component'; export default Component // Multiple components to export from the folder import ComponentA from './ComponentA'; import ComponentB from './ComponentB'; export { ComponentA, ComponentB };
[!example] import in a component
// Without index.js import Component from './components/Component/ComponentFile'; // With index.js import Component from './components/Component'; // add {} if it is exported from an index.js exporting multiple component import { Component } from './components/Common';
JavaScript will automatically look for an index.js
file when you specify the path to a directory. This leads to cleaner and more readable code.
Additionnally, we use @
alias to simplify imports, which is setup through jsconfig.json
and webpack.config.js
at the root of the project. This alias represents the src
directory.
[!example] import using @ alias
import { Component } from '@/components/Common'
This prevents to end up with multiple ../../
when you're importing inside a nested component from a totally different folder such as grand parents or uncles.
jsconfig.json
Information: VSC Documentation about jsconfigwebpack.config.js
Information: Webpack documentation
We use Context API and useReducer
hook for managing state.
-
Context: allows use to pass data down to nested components without having to manually pass props at every level. This allows us to avoid "prop drilling" and makes our code cleaner and more maintainable
-
useReducer:
For more complex state logic — such as the state machine in The Chase — we use theuseReducer
hook. This hook lets us manage state transitions in a more predictable way, through the use of actions and a reducer function.
We primarily use functional components in conjunction with hooks.
useState
: useState DocumentationuseEffect
: useEffect DocumentationuseContext
: useContext DocumentationuseReducer
: useReducer Documentation
As your application grows, it might be helpful to start splitting components into smaller, reusable pieces. But how do you decide when to split a component? Here are some guidelines that can help:
-
Single Responsibility Principle: Each component should ideally do one thing. If it ends up growing, it should be split into smaller subcomponents.
-
Reusability: If a part of your UI is used in multiple places, it's a good idea to make it a separate component. This allows for code reuse and consistency across your app.
-
Complexity: If your component has too many lines of code or is becoming hard to understand, consider splitting it into smaller components. A good rule of thumb is if you have to scroll too much in your editor to see the entire component, it may be a sign that your component needs to be broken down.
-
State Logic: If a portion of your component has its own state or side effects (with hooks like
useState
oruseEffect
), it might be a good idea to split that part into its own component.
Remember, splitting components is an art. And be aware that smaller components are usually easier to maintain and test.
Ideally, we should be integrating more testing into our workflow. We use Jest and React Testing Library.
- Jest: Jest Documentation
- React Testing Library: React Testing Library Documentation
We write our React code in JavaScript and we use the JSX Syntax.
Meaning that JavaScript expressions are embedded inside {}
. JSX will convert HTML tags into React elements.
- JSX: JSX Documentation
Here are some advantages of using JSX instead of JS:
-
Readability and Simplicity: JSX makes our components easier to read and understand. It provides a clear picture of the DOM structure of a component, as it looks like regular HTML.
-
Easy to Identify: In our codebase, we use
.jsx
extension for React components. This makes it easy to identify component files as opposed to other JavaScript files such asindex.js
in components folder or utility JavaScript files. -
Performance: JSX compiles into
React.createElement()
calls which is more efficient than creating elements with JavaScript and appending to the DOM. -
Components as HTML Elements: With JSX, we can use our custom components as if they were HTML elements, which makes the composition of our components a lot more intuitive.
Warning
Remember to import React in every JSX file:
import React from 'react';
To ensure the accuracy of props passed to components, we use PropTypes for type checking in our React codebase. PropTypes exports a range of validators that can be used to make sure the data you receive is valid.
[!example] Example of Type checking using PropTypes
import PropTypes from 'prop-types'; const Component = ({ name, age }) => { return ( <div> <h1>{name}</h1> {age && <p>{age} yo</p>} </div> ); } Component.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, } export default Component;
Here, the Component
expects to receive a name
and an optional age
. If name
prop is not provided or it is of incorrect type, a warning will be shown in the JavaScript console. If age
is provided but is not a number, a warning will also be shown. This helps us catching and fix problems earlier in development.
- PropTypes Documentation: ReactJs PropTypes documentation
While working with our React.JS codebase, there are certain best practices to follow. Including:
-
One component = one file: In React, a component is a reusable piece of code that returns a React element to be rendered to the page. In our codebase, we adhere to the principle of "One Component Per File". This means that each
.jsx
file in our components directory defines exactly one React component. This makes it easy to understand the structure of our application and locate specific components. -
Choosing
useState
versususeReducer
:useState
is enough for simple state management. For complex state logic such as a state machine,useReducer
is a better choice. -
Using Context: Context should be used to pass data down to nested components without having to manually pass props at every level.
-
Naming Conventions:
PascalCase
: For the name of our components — both file name and the functional component itself —camelCase
: For the name of our variables.
This document should give you a better understanding of how we use React.js in our codebase.