Key concepts to know before starting with React

Key concepts to know before starting with React

There are a few key concepts that a frontend developer should know before diving into React.

There are a few key concepts that a frontend developer should know before diving into React.

Array methods

In JSX (JavaScript XML), due to the fact that you're often working within an expression, we can't use a regular for/foreach/for..of construct to transform a sequence of data into usable DOM elements or specialized components. Instead, we have to use map() or filter() to accomplish our tasks.

map(), the most used array method in React, usually comes in the code with the following schema:

  1. You start with an array, usually taken from the current component's state.
  2. Now you can use map() but it's very important at this point that you've previously set an empty array as a default initial value up in the state. Otherwise, you'll get an error.
  3. If you use the callback function with only one parameter, then this parameter represents the current array's item (e.g. a JSON object fetched from a REST API. Depending on the number of properties of such an object, you may choose to use object destructuring in order to have all the values as variables available in the current callback's scope).
  4. Finally, your callback function will return an expression where you can write your elements with JSX variables or use your custom components.

Let's see an example:

import { useState, useEffect } from 'react';
import axios from 'axios';

export default function Test() {
    // We start with an empty array
    const [items, setItems] = useState([]);
    
    useEffect(() => {
        axios.get('/api/items').then(resp => {
            setItems(resp.data);
            // Now items[] has been populated
        });
    }, []);
    // Returns an expression. Note '(...)'.
    return (
        <ul className="items">
            {items.map(({ name, price, id }) => (
               <li key={id}> 
                   { name } / { price }
                 </li>   
            ))}
        </ul>  
    );
}

At this point we have the component <Test />. In this case, we fulfill the React requirement of having a unique key for each element in a sequence by using the id property of each item. If our items lack of a unique identifier, we can use the array index passed as the second argument of the callback function:

return (
        <ul className="items">
            {items.map(({ name, price }, index) => (
                 <li key={index}>
                   { name } / { price }
                 </li>   
            ))}
        </ul>  
);

Some of you may be tempted to write a raw function that generates a pseudo-random string in order to fulfill the React's requirement but this is far from being ideal.

Modules

ES6 Modules are used in React during the development process, namely before you're done with your app and you can build all of your JavaScript files into a single bundled file.

Broadly speaking, using modules creates a communication channel between several JavaScript files. There's always a file that imports (the import keyword) one or more functions or objects from other files. In turn, the target files (specified as a path by using the from keyword) must export (the export keyword) the functions or objects you want to import.

In short, you usually import Something from "./Something" (the .js extension has been omitted) in a file (component). In turn, within Something.js you may have export default Something at the very end of the file. This is the simplest scenario when you have only one function or object exported. Another example is when you have several functions in a module:

// Amodule.js

export function randomKey() {
    return Math.random().toString().substring(1);
}

export function currentDate() {
    return new Date().toLocaleString();
}

Then we can import the above functions as follows:

import { randomKey } from './Amodule';

console.log(randomKey())

The from keyword in React consists of relative paths for user-defined modules (like those we've seen above) or absolute names if a module has been installed via NPM. For example:

import { Routes, Route } from 'react-router-dom';
import axios from 'axios';

These components and objects are globally available via NPM so you don't need to specify a path. Bear in mind that you'll deal with ES6 modules only during the development process: when you build your project, the final code will be bundled into a single JavaScript file.

ES6 modules are quite a "new" feature for most developers who 99% of the time use script elements, but if you stick to the official documentation and tutorials, you'll be just fine. During the development process, Webpack will guide you by sending useful messages to the console so that if you're stuck, you can always google the error or warning message to get more information about your problem and find a solution.

Components and the DOM

React has been created to manage user interfaces by splitting them into several reusable components. Each component is a custom element (like <Test /> seen above) whose DOM representation is converted in a more canonical DOM structure. For example, our sample component may be rendered (or thought) as follows:

<ul class="items">
    <li>Sample 1 / $ 0.50</li>
    <li>Sample 2 / $ 1.99</li> 
    <li>Sample 3 / $ 0.70</li>   
</ul>

Since the DOM is based upon a tree hierarchy and a relationship between parent and child elements, React adheres to the DOM specifications by creating a hierarchy of components but with a strict rule when it comes to JSX: if there are siblings, these must always have a parent element. Let's get back for a moment to our previous example. One cannot write two sibling elements like this:

return (
        <h1>Items</h1>
        <ul className="items">
            {items.map(({ name, price }, index) => (
                 <li key={index}>
                   { name } / { price }
                 </li>   
            ))}
        </ul>  
);

h1 and ul must be wrapped within a parent element. You can either choose to use an actual element or a generic wrapper (<></>), like this:

return (
    <>
        <h1>Items</h1>
        <ul className="items">
            {items.map(({ name, price }, index) => (
                 <li key={index}>
                   { name } / { price }
                 </li>   
            ))}
        </ul> 
    </>     
);

As long as you understand this parent-child relationship, you won't have any problems with creating your React components. But how do components work? If you take another look at our first example, you'll notice that in this case a component is just a function that returns an expression. If you name your function as Test, then you can use a component named <Test /> elsewhere in your code just like any other DOM element (<p>, <div>, etc.).

A component may or may not have children. Since <Test /> here has no descendants, we must follow the XML rule adopted by JSX and use the /> syntax with empty elements. This rule also applies to normal HTML elements, such as br, input, hr and img.

As said earlier, we have just created a function. Internally, React passes to our function an argument commonly known as props, which is an object containing both default properties and custom properties. For example, if we want to create a wrapper component for our <Test/> component, we can make use of the default property children that allows our wrapper to host descendants.

export default function Wrapper(props) {
    return (
        <div className="wrapper">
            { props.children }
        </div>
    );
}

Now we can use our wrapper component with our <Test /> component:

import Wrapper from './components/Wrapper';
import Test from './components/Test';

export default function App() {
    return (
        <Wrapper>
            <Test />
        </Wrapper>
    );
}

We can now view our overall structure as follows:

<div class="wrapper">
    <ul class="items">
        <li>Sample 1 / $ 0.50</li>
        <li>Sample 2 / $ 1.99</li> 
        <li>Sample 3 / $ 0.70</li>   
    </ul>
</div>

We mentioned before the props object. This object is crucial for making components communicate to each other. By design, each component is isolated and has its own state. The useState() function is called a hook in the React terminology and defines a local property/variable and its setter function.

const [items, setItems] = useState([]);

With this declaration, we define the items array and its setter function setItems() via array destructuring. The empty array passed as an argument to useState() defines the initial value (or state) of items, that is, an empty array.

Bear in mind that without the setter function setItems() we can't set or override the initial value of items directly. In fact, let's see again how we can use this setter function within the useEffect() React's hook:

useEffect(() => {
        axios.get('/api/items').then(resp => {
            setItems(resp.data);
            // Now items[] has been populated
        });
}, []);

As the React's documentation states:

What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.

Here the key thing to understand is that without the call to setItems() after our API request, items will remain empty so that our list won't have any items inside. In HTML terms, we would have:

<ul class="items"></ul>

So, how can components communicate to each other if their states are isolated? The answer is simple: via the custom properties we define in a parent component. Let's say that we want to pass a value from the state of a parent component to the <Test /> component. First, we need to define our value in the parent component.

import { useState, useEffect } from 'react';
import Wrapper from './components/Wrapper';
import Test from './components/Test';

export default function App() {
    const [title, setTitle] = useState('');
    // useEffect() here is used only for consistency
    useEffect(() => {
         setTitle('Items available')
    }, []);
    return (
        <Wrapper>
            <Test />
        </Wrapper>
    );
}

Now we can pass the title value as a custom attribute to <Test/>. This change will make the title property available in the props object of Test.

import { useState, useEffect } from 'react';
import Wrapper from './components/Wrapper';
import Test from './components/Test';

export default function App() {
    const [title, setTitle] = useState('');
    // useEffect() here is used only for consistency
    useEffect(() => {
         setTitle('Items available')
    }, []);
    return (
        <Wrapper>
            <Test title={title} />
        </Wrapper>
    );
}

Finally, in the Test function we can use this property as follows:

import { useState, useEffect } from 'react';
import axios from 'axios';

// We destruct props to get the title property



export default function Test({ title }) {
    // We start with an empty array
    const [items, setItems] = useState([]);
    
    useEffect(() => {
        axios.get('/api/items').then(resp => {
            setItems(resp.data);
            // Now items[] has been populated
        });
    }, []);
    // Returns an expression. Note '(...)'.
    return (
      <>
        <h1>{title}</h1>
        <ul className="items">
            {items.map(({ name, price, id }) => (
               <li key={id}> 
                   { name } / { price }
                 </li>   
            ))}
        </ul>
      </>    
    );
}

In HTML terms this will be:

<div class="wrapper">
    <h1>Items available</h1> 
    <ul class="items">
        <li>Sample 1 / $ 0.50</li>
        <li>Sample 2 / $ 1.99</li> 
        <li>Sample 3 / $ 0.70</li>   
    </ul>
</div>

Properties can also be references to functions, and this is a way by which we can handle DOM events if it makes sense from a design perspective that certain actions should be handled by a parent component.

Documentation

  1. Introducing JSX
  2. Getting Started with React
  3. JavaScript modules
  4. Introducing Hooks
  5. Create a New React App