React logo

1. Keep the project structure and files organized.

A basic principle that you should apply to any project no matter the language is to keep the project folders organized. This makes more sense for bigger projects where a team is working together.

Components must be grouped by some criteria like their functionality, behavior, section of the application they are used, relationship between components, etc. In Anden Solutions we group components by section (or pages) where they are used. Shared components get stored in a generic/utils folder.

Second, put some thoughts naming each component. The name should clearly describe the component’s functionality or goals. Since most projects are a result of a team collaboration, using some common sense in the naming goes a long way. Undoubtedly, this is a benefit for the whole team, especially when on-boarding new developers.

2. Design atomic components

When creating a new component bear in mind its scope and goals. Develop generics and reusable components. In other words, don’t develop a big component that includes tons of goals. Try to divide them into smaller ones.

3. Pay attention to the type and name of variables

Let’s start with “what is a variable”? Variables are “containers” used to save data or values. We can call them whatever we want, except for reserved words. Javascript uses three type of variables: var, let, and const. For now we’ll focus on the first two

-VAR: The var statement declares a function-scoped or globally scoped variable, optionally initializing it to a value. It’s important to point out that the hoisting will affect the variable declaration, but not its value’s initialization. The value will be indeed assigned when the assignment statement is reached.

-LET: The let statement declares a block-scoped local variable, optionally initializing it to a value.

Lets see them in action:

var a = 5;
var b = 10;
if (a === 5) {
	let a = 4; (the scope is inside the “if” block)
	var b = 1; (the scope is global)
	console.log(a); (returns 4)
	console.log(a); (returns 4)
}
console.log(a); (returns 5)
console.log(b); (returns 1)

Before declaring a new variable, you need to think about its scope, what it will be used for, so you can decide the type of variable you need. Then, select a name that properly describes the data the variable contains.

4. Use Hooks

Hooks were added to React int the version 16.8, but that doesn’t mean that you will need to rewrite all your all code. Hooks are optional, because the new React versions with hooks is 100% backward-compatible.

Hooks provide a lot of benefits, you’ll get a cleaner code, easier to maintain and read when compared with class components.  

Is hard to reuse or share the logic between class components, but this is way easier with hooks. This is helpful when we get a large component with a lot of logic. In the following code we can compare the differences between a class component and a function component.

// Class Components

constructor(props) {
    super(props);
    this.state = {
       userId: null,
       data: [],
    };
}

componentWillMount() { 
    if (this.props.userId) { 
       let userData = this.props.getUserData(this.props.userId); 
       this.setState({data: userData, userId: this.props.userId)}) 
    } 
} 

componentDidMount() { 
    window.scrollTo(0, 0); 
} 

componentWillReceiveProps(nextProps) { 
    if (nextProps.userId !== this.state.userId) { 
       let userData = this.props.getUserData(nextProps.userId); 
       this.setState({data: userData, userId: nextProps.userId)}, () => window.scrollTo(0, 0))    
    }  
} 

The class component has different methods to handle changes of state, new properties, and/or re-renders such as componentWillMount, componentWillReceiveProps, componentDidMount, etc. In the above listed example, when the class receives new props, it sets the state of the data and userId variables and then scrolls to the top. Since the change could happen on any prop, our code will need to compare each one of them to identify what the right behavior should be.

// Function Components (Hooks) 

const [ userId, setUserId] = setState()
const [ data, setData] = setState()

useEffect(()=>{ 
   if (props.userId) { 
      let userData = props.getUserData(props.userId); 
      setUserId(props.userId)) 
      setData(userData) 
    } 
},[props.userId]) 

useEffect(()=>{ 
   window.scrollTo(0, 0) 
},[userId]) 

The useEffect hook in the function component above can be used to replace the componentWillMount, componentDidMount, and componentWillReceiveProps functions in the class component. The useEffect hooks is called every time the function is loaded or any time a parameter changes.

The Function Component example also shows how we can split the business logic for each variable into as many hooks as needed. In this case the first useEffect hook waits for a change in props.userId, while the second one is listening to the userId const, which is actually set in the first hook. This is an example of how hooks could make a component cleaner and efficient and why we prefer them.

5. Use Hooks properly 

There are a few rules that we must keep in mind when using Hooks.

  1. Call Hooks at the Top Level.
  2. Don’t use hooks from a React class component or JavaScript functions.

For detailed information and exaples click here.

6. Think about the project scalability

On smaller sized projects, the components can handle the data they use, making all the endpoints calls to the APIs. However, when a project grows, it is advisable to use new patterns to handle shared data, such as Redux, MobX, GraphQL, Context API. 

Each of these patterns has their own way to share and store the application’s data. By using these pattern we end up with a cleaner code, improved  performance, and easier to maintain code.

Another good libraries that support scalability are the Sagas or Thunk middlewares. These libraries give you total control over the flow of your application code, preventing side effects like callbacks hell.