Blog

  • Hello world!

    Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

  • Object Destructuring in Javascript

    In javascript, objects are commonly used to store multiple data in a single variable. This provides a very compact and convenient way to operate on the data, especially, when passing the data to other parts of the application code. Javascript provides a very convenient means to extract the value of each data when needed.

    In Javascript, we can use the object destructuring method to extract the values of multiple properties from an object in just a single statement. It eliminates the need to for multiple statements when accessing the values of multiple object properties. The object destructuring approach has less typing and saves time.

    Javascript object destructuring can also be used to perform other object operations such as cloning, extending, updating, and merging other objects.

    This post takes an in-depth look at ES6 Javascript object destructuring technique, with emphasis on clarity and working examples for better understanding.

    Object Destructuring Assignment

    In Javascript, object destructuring assignment is an expression that makes it possible to extract some or all properties of an object into specific variables. It was introduced in ES6 (ECMAScript 2015) as a means to simplify the extraction of properties from objects and arrays. It can also be used to destructure strings.

    Before its introduction, we would extract the values of object properties by typing the name of the object variable followed by the dot (.) operator, and then the property we want to access.

    For example, let’s suppose we have an object data that contains information about a user:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };

    We can access the values of the object properties using the dot (.) notation:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // extract values
    const name = User.name;
    const email = User.email;
    const role = User.role;

    Another approach involves specifying the property names as a string enclosed in square brackets, [], following the property name:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // extract values
    const name = User['name'];
    const email = User['name'];
    const role = User['name'];

    Both approaches, however, involve more typing to access property values. For example, each access to a property value requires its own statement. Additionally, we always need to type the object variable name, square brackets, and property name in each statement to access a property value. All these lead to more typing, especially when we have to access multiple object property values.

    But ES6 Javascript has a better and compact approach to do this, called object destructuring. With ES6 Javascript object destructuring, we can extract all or some of the property values of the User object in just a single statement:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // extract values
    const { name, email, role } = User;

    Line 9 in listing 4 does the trick. It is called object destructuring assignment. In just a single statement, we have been able to extract all the property values that we need. We did not have to type multiple object variable name, square brackets, and property keys. This approach is much of less typing and saves time.

    In the discussions that follow, we will delve deeper into Javascript object destructuing. Before that, we should look at the very basic syntax of Javascript object destructuring assignment.

    Object Destructuring Assignment Syntax

    In Javascript object destructuring assignment, an object from which we want to extract property values is assigned to declared variables. The declaration variables into which the values of the object properties are stored are enclosed in braces, {}, on the left-hand side of the assignment operator.

    For example, in listing 4, the statement on line 9 is object destructuring assignment. In this assignment, the User object is destructured into individual properties, and the values of these properties are assigned to the declaration variables enclosed in curly braces.

    Unlike in Javascript array destructuring, the order of the declaration variables is not important in object destructuring. Shortly, we will know how Javascript is able to tell which property values to store in each of the declaration variables.

    Looking again at line 9 in listing 4, we can tell that an object destructuring assignment has the following basic syntax:

    JavaScript
    // destructure object into variables
    let { var1, var2, var3 } = ObjectData

    In this assignment syntax, var1, var2, and var3 are declaration variables into which the values of properties in ObjectData will be stored. As seen, the declaration variables are enclosed in braces, {}. This is in contrast to array destructuring assignment syntax, which uses square brackets, [], to enclose declaration variables.

    We can even use the const declaration keyword for the declaration variables:

    JavaScript
    // destructure object into variables
    const { var1, var2, var3 } = ObjectData

    Understanding Object Destructuring

    Javascript object destructuring is a programming technique that makes it possible to extract multiple object property values into variables a single statement. It eliminates the need for dedicated statements for each property value access, and has less typing than property access with the dot notation.

    What makes this approach so handy is that we can declare multiple variables and use the object variable name only once in an assignment. Javascript does the job of extracting the property values into these declared variables.

    Consider the code listing below:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };

    With Javascript ES6 / ECMAScript 2015, we can extract muiltiple properties into variables in just a single statement whiles using the User object name only once in an assignment:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // extract property values into variables
    const { name, email, role } = User;
    
    console.log(name);   // output: Daniel
    console.log(email);  // output: dan@mail.com
    console.log(role);   // output: Developer

    The order of the variables in the declaration does not matter. We can declare the target variables in any order:

    JavaScript
    // extract property values into variables
    const { role, name, email } = User;
    
    console.log(name);   // output: Daniel
    console.log(email);  // output: dan@mail.com
    console.log(role);   // output: Developer

    If the order of the declaration variables does not matter, then how is Javascript able to map the declaration variables to the object properties and extract their values? The answer is simple. By default, Javascript expects the declaration variable names to match property keys in the object being destructured.

    For example, in the object destructuring statement,

    JavaScript
    const { name, email, role ] = User;

    the declared variable name, email, and role match property key names in the User object.

    JavaScript
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };

    By this, Javascript is able to map the variable names to the object property keys and extract their values. Later, we will see how we can specify variable names that are not property keys in the object being destructured.

    Remember from our discussion on destructuring assignmnent syntax that we use braces, {}, to declare target variables when destructuring an object. By default, the variable names enclosed in the braces are the names of the properties that we want extract.

    If a variable name exists in the destructuring declaration but is not found as a property or key in the object being destructured, then the value for that variable will be undefined. We delve more into this next.

    Mismatch Variable Name in Property Keys is undefined

    We have indicated that, in object destructuring, the variable names in the destructuring declaration should match property keys in the the object that is being destructured. We have already seen this in listing 8.

    But what if we type a declaration variable name which does not exist as a key in the properties of the object being destructured? Well, let’s see what happens.

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    const { username, email, role } = User;
    
    // log values
    console.log(username);  // output: undefined
    console.log(email);     // output: dan@mail.com
    console.log(role);      // output: Developer

    On line 8, the username variable in the declaration variables does not match any name in the User object property keys. The remaining declaration variable names, email and role, however have matching names in the User object property keys.

    Since username does not match any property key in the User object, there will be no value to extract into the username variable. Therefore its value will be undefined, as seen in its output on line 11.

    Thus, if variable name in the destructuring declaration is not found as a property key in the object being destructured, then the value of that variable will be undefined.

    Setting Default Values

    If there is a possibility for the value of a property to be undefined, then we can set a default value that should be used. We do this by assigning the default value to the target variable in the destructuring statement. The following listing is an example:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: undefined
    };
    
    const { name, email = 'user@mail.com', role = 'Data Analyst' } = User;
    
    // log values
    console.log(name);    // output: Daniel
    console.log(email);   // output: dan@mail.com
    console.log(role);    // output: Data Analyst

    On line 6, we have deliberately set the value of the role property to undefined. In the variables declaration on line 8, we have set default values for email and role. After object destructuring, we can see from the log values that the default value for role has been used since the extracted value from the role property in the User object is undefined.

    The default value for the variable email, which is user@mail.com, was not used since the email property of the User object is not undefined. Thus, the default value is used when the value extracted from an object property is undefined.

    Destructuring Into Existing Variables

    In the examples that we have covered so far, we have extracted the property values into newly declared variables:

    JavaScript
    // delcare variables
    const { name, email, role } = User;

    The const keyword preceding the declaration variables is an indication that we are declaring new variables.

    If we have existing variables whose names match property key names in the object, then we can specify the existing variables in the destructuring statement. In this case, there will be no need to precede the destructuring statement with const or let. However, there is one more thing to take into account. We will need to enclose the whole statement in parenthesis before that will work:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // variables declration
    let name, email, role;
    
    // destructure object into the existing variables
    ({ name, email, role } = User);

    The variables name, email, and role are declared on line 9. Then on line 12, we specify these existing variables in the destructuring statement. You should observe that we have enclosed the destructuring assignment in parenthesis, (). Without the parenthesis, an error will be generated.

    For example, the following statement destructures the User object into existing variables without the statement being enclosed in parenthesis:

    JavaScript
    // destructure object into the existing variables
    { name, email, role } = User;

    Destructuring an object into existing variables without the statement being enclosed in parenthesis, like on line 2, will generate the following error:

    If we intend to destructure an object into already declared variables, we will need to enclose the destructuring assignment expression in parenthesis, ().

    Aliasing Declaration Variable Names

    In listing 8, we understood that, by default, we are compelled to declare variables with names that match object property key names when destructuring an object. If a variable name in the declaration does not match a property key in the object being destructured, its value will be undefined.

    But what if the object property keys are not descriptive enough to be used as variables in the current scope? Or what if we prefer a different name for the variable?

    Consider the following object data:

    JavaScript
    // object declaration
    const User = {
        n: 'Daniel',
        e: 'dan@mail.com',
        r: 'Developer'
    }

    Some applications that transmit data through networks use shortened names rather than descriptive and long property key names. The aim may be to reduce the size of data transmitted over the network.

    If we are to destructure an object with non-destructive property keys, such as in listing 16, by default, we will be compelled to use these non-descriptive property keys as variables in the application.

    JavaScript
    // initialize object data
    const User = {
        n: 'Daniel',
        e: 'dan@mail.com',
        r: 'Developer'
    };
    
    const { r, e, r } = User;
    
    // log values
    console.log(name);    // output: Daniel
    console.log(email);   // output: dan@mail.com
    console.log(role);    // output: Developer

    Luckily, ES6 Javascript allows us to specify alias names for object property keys. We can use these alias names to refer to the properties in the object being destructured.

    To specify an alias name when destructuring an object, we separate the declaration variable name and the alias name with a colon, :. For example:

    JavaScript
    // initialize object data
    const User = {
        n: 'Daniel',
        e: 'dan@mail.com',
        r: 'Developer'
    };
    
    const { n: name, e: email, r: role } = User;
    
    // log values
    console.log(name);    // output: Daniel
    console.log(email);   // output: dan@mail.com
    console.log(role);    // output: Developer

    Once an alias name is specified, we cannot use the actual variable name in the application logic. For example, the following will generate an error:

    JavaScript
    console.log(n);

    This produces a ReferenceError. Since we have specified an alias name for the variable n, we can no longer use it in the application scope where it is declared. To Javascript, such variable does not exist:

    A problem arises if a property that needs to be extracted has the same name as a variable already declared in the current scope. Consider the following code listing:

    JavaScript
    // email variable declration
    let email = 'default@mail.com';
    
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // extract values from the User object
    const { name, email, role } = User;

    This code listing will generate a syntax error when run. A sample error can be seen below:

    The User object contains a property with the key email. Meanwhile, in the destructuring assignment on line 12, the declaration attempts to create variables, including email, But email is an existing variable declared on line 2. This generates the syntax error since a variable with name email already exists.

    The solution to such problem depends on whether we can overwrite the value in the existing variable, email, or not.

    If we do not want to overwrite the value in the existing variable, then we will need to provide an alias name for the email property:

    JavaScript
    // extract values from the User object
    const { name, email: userEmail, role } = User;
    
    // log values
    console.log(email);      // output: default@mail.com
    console.log(userEmail);  // output: dan@mail.com

    However, if we wish to overwrite the value in the existing variable, email, then we will need two statements to extract the values: one to declare the new variables for assignment, and the other to assign to the existing variable:

    JavaScript
    // extract into existing variable
    ({ email } = User);
    
    // extract into new variables
    const { name, role } = User;

    Since email is already declared, we only perform the assignment by enclosing the statement in parentheses. However, name and role are new variables and therefore we declare them with the const keyword. We could also have used let or var to accomplish same.

    Extracting Rest of Properties into a Variable

    Sometimes, we may wish to extract the values of some properties into specific variables and then pack the rest, or remaining properties into another variable. ES6 Javascript provides the rest operator to do this.

    The rest operator is a three consecutive dot (...) operator that is used to precede a declaration variable name to signify that the remaining properties that have not been extracted yet, should all be extracted and packed into the variable preceded by the rest operator.

    For example, if we want to extract the name property into a variable and extract the remaining properties into another variable, we will have to use the following destructuring statement:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    const { name, ...theRest} = User;
    
    console.log(name);     // output: Daniel
    console.log(theRest);  // output: { email: 'dan@mail.com', role: 'Developer' }

    On line 8, the value of the name property of the User object is extracted into the name declaration variable. The rest operator, (...), preceding theRest variable implies that, the remaining properties which have not been extracted yet should all be extracted and packed into theRest variable.

    With the rest operator preceding theRest variable, Javascript packs the remaining properties into the variable for us. One important thing to note is that the variable preceded with the rest operator must be the last element in the declaration variables. The following statement is wrong and will generate a syntax error:

    JavaScript
    const { ...theRest, name } = User;

    The following syntax error will be generated since the rest operator is applied to a variable element which is not the last:

    Another important thing to note is that, the variable into which the properties are packed is always an object. This is so even if there is only one property packed into it. Let’s see an example below:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    const { name, email, ...theRest} = User;
    
    // log value of theRest
    console.log(theRest);  // output: { role: 'Developer' }

    On line 8, we apply the rest operator to theRest variable. At this point, the name and email have already been extracted, with only role remaining and packed into theRest. The log on line 11 reveals that theRest is actually an object, although only one property was extracted into it. Thus, if we precede a declaration variable with the rest operator, then the type of the variable is always an object.

    Using Spread Syntax in Object Destructuring

    The spread syntax is ES6 Javascript expression that expands the properties of an object into individual parts. Like the rest syntax, the spread syntax is a three consecutive dot (...) operator preceding an object variable that expands the properties of the object.

    The spread syntax can be used to perform operations such as merging multiple objects, cloning, updating, and extending an object.

    Cloning Objects

    The ES6 Javascript spread syntax can be used to make a shallow clone of an object so that an update to one object reference does not affect the other object.

    Suppose we have the following object data:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };

    If we assign the User to another variable, both variables will have the same reference to the object data.

    JavaScript
    // assign the User object to another variable
    const anotherUser = User;
    
    console.log(anotherUser === User);  // output: true

    In this case, an update to the data using one variable will affect the other variable. To make a shallow clone of the User object so that they will have the same data but different references, we will need to clone the User object.

    With ES6 Javascript, we can use the spread syntax to clone the User object.

    JavaScript
    // make a copy of the User object
    const anotherUser = {...User};

    The spread operator expands the properties of the User object and places them between the braces, {}. This creates a new object with a different reference, and the newly created object is assigned to anotherUser.

    Although User and anotherUser have the same data values, they have different references, and an update to one does not affect the other. Thus, we can use the ES6 Javascript spread syntax to clone an object.

    Extending Objects

    We can use the ES6 Javascript spread syntax to extend an object. This is particularly helpful if there are multiple properties to be added to an existing object data.

    In the pre-ES6 era, we could do this by using the dot (.) notation to extend an object with new properties, or use Object.assign() to add new properties:

    JavaScript
    // initialize an object
    let data = {
        name: 'Daniel'
    };
    
    // add new properties
    data.email = 'dan@mail.com';
    data.role = 'Developer';

    The following code listing uses the spread syntax to extend an existing object:

    JavaScript
    // initialize an object
    let data = {};
    
    // add new properties
    data = { ...data, email: 'dan@mail.com', role: 'Developer' };

    Line 5 initializes a new object by spreading or expanding the current properties of data, and then includes email and role properties in the new object. In this example, we assign the newly initialized object to data. Sometimes, we may need to assign the newly initialized object to a different variable.

    Updating Objects

    Although we can use the dot (.) notation to update object property values, if we need to update multiple values, we will need multiple statements to do this:

    JavaScript
    // initialize object data
    let User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // update property values with dot notation
    User.name = 'Emmanuel';
    User.email = 'emma@mail.com';

    Before ES6 Javascript, we could also achieve this in a single statement using Object.assign() method. However, we can use the spread syntax to achieve the same in just a single statement:

    JavaScript
    // initialize object data
    let User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };
    
    // update property values with dot notation
    User = { ...User, name: 'Emmanuel', email: 'emma@mail.com' };
    
    console.log(User); // output: { name: 'Emmanuel', email: 'emma@mail.com', role: 'Developer' }

    Merging Objects

    If we have multiple objects that we need to merge into one object, we can easily do so using the Javascript spread syntax. To do this, we will need to initialize a new object and spread the properties of the objects that we need to merge in the initializer object.

    For example, suppose we have the following objects, A and B:

    JavaScript
    // Object A
    const A = {
        name: 'Daniel',
        email: 'dan@mail.com'
    };
    
    // object B
    const B = {
        role: 'Developer',
        address: {
            city: 'Kumasi',
            region: 'Ashanti'
        }
    }

    We can merge objects A and B into another object using the spread syntax. To do this, we spread the properties of objects A and B in a new object that we initialize:

    JavaScript
    // merge A and B into a new object
    const User = { ...A, ...B };
    
    // log User object
    console.log(User);

    From the log output below, we can see that the User object contains all the properties of objects A and B:

    The properties of objects A an B were merged into the User object.

    Destructuring Function Parameters

    ES6 Javascript provides a very convenient way to destructure object parameters in a function. That is, we can destructure function parameters that are objects.

    Before ES6 Javascript, we would access object properties from function parameters such as in the following code listing:

    JavaScript
    function showUser(userParam) {
        let name = userParam.name;
        let email = userParam.email;
        let role = userParam.role;
        
        // log values
        console.log(name);    // output: Daniel
        console.log(email);   // output: dan@mai.com
        console.log(role);    // output: Developer
    }

    Rather than access the property values into new variables, we can destructure and bind the parameter object properties into new variables directly in the function header: To understand this better, we should understand that when we pass a value to a function, the argument value is assigned to the function parameter.

    Given the User object used in earlier examples, we can pass it to the showUserInfo() function as follows:

    JavaScript
    showUserInfo(User);

    As indicated earlier, passing a value to a called function assigns the function argument to the function parameter, as if we have done the following:

    JavaScript
    userParam = User;

    userParam is the parameter variable in the function header, and User is the object passed as argument. But since User is an object, we can destructure it into defined variables, as we have already been doing, as follows:

    JavaScript
    { name, email, role } = User;

    With ES6 Javascript, we can replace userParam in the function header with the declaration variables { name, email, role }. Thus, we can rewrite the showUserInfo() function as follows:

    JavaScript
    function showUserInfo({ name, email, user }) {
        // log values
        console.log(name);    // output: Daniel
        console.log(email);   // output: dan@mai.com
        console.log(role);    // output: Developer
    }

    As can be seen, destructuring object parameters in function headers has less typing and saves time.

    Destructuring Nested Objects

    We can extend the understanding gained so far in Javascript object destructuring to destructure objects nested within an object. Destructuring nested objects is simple, although it may seem challenging at first.

    Let’s revisit our User object data which we have used for the examples in previous discussions:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer'
    };

    We will extend this User object by adding an address property:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer',
        address: {
            city: 'Kumasi',
            region: 'Ashanti'
        }
    };

    We can identify that the value of the address property is an object. Thus, we have an object within an object. Let’s suppose that we want to extract city, and region into variables.

    In the pre-ES6 Javascript era, we could extract the city and region from the address property using the dot notation, such as in the following code listing:

    JavaScript
    // extract city and region
    let city = User.address.city;
    let region = User.address.state;

    With ES6 Javascript object destructuring, we can extract these values into variables in just a single statement. We will take step by step approach to better understand nested object destructuring.

    When destructuring a nested object, we need to specify the property key name followed by colon, :, then the declaration variables enclosed in braces:

    JavaScript
    const { nestedObjectProperty: { DECLARATION_VARIABLES }};

    Think of the specification of the property key name as directing Javascript to the nested object which we want to destructure.

    For example, in our User object in listing 41, the nested object has the property key name address, hence we will specify this property key name, followed by colon, ( : ), then the declaration variables:

    JavaScript
    const { address: { DECLARATION_VARIABLES }} = User;

    One key trick in destructuring nested objects is to isolate the nested object to be destructured, whiles taking note of its property key name. In our example, the value of the address property is an object, and we can isolate the address object as follows:

    JavaScript
    address = {
        city: 'Kumasi',
        state: 'Ashanti'
    }

    Given this address object, we can use our normal destructuring technique to extract the values of city and region into variables as follows:

    JavaScript
    const { city, region } = address;

    As can be seen, destructuring the address property in isolation gives us { city, region }, which we can use to replace { DECLARATION_VARIABLES } in listing 41.

    JavaScript
    const { address: { city, region }} = User;

    That is really simple, isn’t it?. The only extra typing here is the specification of the address property key name. It is however important since this is a clue to Javascript to know which specific nested object to look and extract property values.

    The following code listing is a working example of what we just covered:

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer',
        address: {
            city: 'Kumasi',
            region: 'Ashanti'
        }
    };
    
    // extract city and region from User object
    const { address: { city, region }} = User;
    
    console.log(city);   // output: Kumasi
    console.log(state);  // output: Ashanti

    In another example, we will attempt to extract name, email, and city from the User object. We should remember that city is found in a nested object with property name address, which is found in User object.

    The following statement extracts name, email, and city from the User object.

    JavaScript
    // extract name, email, and city
    const { name, email, address: { city }} = User;

    Since name and email are not properties in nested object, we do not precede their declaration variables with property key names. However, city is a property found in a nested object, hence we precede the declaration variable with address:. As indicated earlier, this a hint to Javascript to know where to perform the nested object destructuring.

    Let us consider another example that is also simple to do, but maybe a bit challenging. We will introduce a location object, nested in the address object, which is also nested in the User object.

    JavaScript
    // initialize object data
    const User = {
        name: 'Daniel',
        email: 'dan@mail.com',
        role: 'Developer',
        address: {
            city: 'Kumasi',
            region: 'Ashanti',
            location: {
                street: 'XYZ Avenue',
                house: 'P8 BLK5'
            }
        }
    };

    We have added a location object, spanning from line 9 to line 12, to the address object which is also nested in the User object. If we need to extract the values of name, city, and street properties, the following statement will do:

    JavaScript
    // extract name, city, and street 
    const { name, address: { city, location: { street }}} = User;
    
    // log values
    console.log(name);    // output: Daniel
    console.log(city);    // output: Kumasi 
    console.log(street);  // output: XYZ Avenue

    Let’s break down the object destructuring statement on line 2 to have a better understanding.

    We can identify from the User object in listing 50 that, the value of name property is basic type, string in this case. Since this is not a nested object, we can extract its value directly without preceding it with a property name:

    JavaScript
    // extract name, city, and street 
    const { name, ... } = User;

    city is a property found in a nested object with property name address. To extract its value, we need to specify the property key name address, and separating the declaration variable with a colon ( : ):

    JavaScript
    // extract name, city, and street 
    const { name, address: { city } } = User;

    Lastly, we can identify that the street property is found in the location object. Since location is a nested object, we will specify its name and the declaration variable as: location: { street }.

    But location is nested in address object. We will therefore need to include it in the declaration for the address property. Thus, we will have the following destructuring statement:

    JavaScript
    // extract name, city, and street from User
    const { name, address: { city, location: { street }}} = User;
    
    // log values
    console.log(name);    // output: Daniel
    console.log(city);    // output: Kumasi 
    console.log(street);  // output: XYZ Avenue

    The destructuring statement on line 2 extracts name, city, and street from User.

    Summary

    Javascript object destructuring is a programming expression that extracts the values of object properties into declared variables. It can be used to extract the values of multiple object properties into variables in a single statement.

    JavaScript
    const Student = {
        id: 1,
        name: 'Emmanuel',
        email: 'emma@mail.com'
    }
    
    // extract property values in a single statement
    const { id, name, email } = Student;

    By default, Javascript expects the declaration variable names to exist as property keys in the object being destructured. If a declaration variable name does not exist as a property key in the object being destructured, then the value of the variable will be undefined:

    JavaScript
    const Student = {
        id: 1,
        name: 'Emmanuel',
        email: 'emma@mail.com'
    }
    
    // extract property values in a single statement
    const { stdId, name, email } = Student;
    
    // log stdId
    console.log(stdId);  // outut: undefined

    As can be seen, the first declaration variable has the name stdId. However, the Student object does not contain a property key with this name. Therefore, the value of stdId will be undefined since it does not have a matching property key name in the Student object.

    If we wish to use a different name as variable than the property key name in the object, then we can provide an alias name to be used:

    JavaScript
    const Student = {
        id: 1,
        name: 'Emmanuel',
        email: 'emma@mail.com'
    }
    
    // extract property values in a single statement
    const { id: stdId, name, email } = Student;
    
    // log stdId
    console.log(stdId);  // output: 1

    On line 8, we specify an alias name, stdId, for the id property in Student object. The value of the id property is extracted into stdId. When we set an alias name, we cannot use the property key as a variable for the same value: An attempt to do this leads to a ReferenceError:

    JavaScript
    // log id
    console.log(id);  // ReferenceError: id is not defined

    When destructuring an object, we can set default values for variables into which extracted values are saved. The default values are used if the value of the variables are undefined after destructuring.

    Consider the following code listing:

    JavaScript
    const Student = {
        id: 1,
        name: 'Emmanuel',
        email: 'emma@mail.com'
    }
    
    // extract property values in a single statement
    const { id, name, email, programme } = Student;
    
    console.log(programme);  // output: undefined

    Observe that the fourth declaration variable, programme, does not exist as a property key in the Student object. As we have already indicated, the value of programme will be undefined. We can specify a default value that should be used rather than undefined by assigning the default value to the variable in the destructuring statement:

    JavaScript
    const Student = {
        id: 1,
        name: 'Emmanuel',
        email: 'emma@mail.com'
    }
    
    // extract property values in a single statement
    const { id, name, email, programme = 'Pharmacy' } = Student;
    
    console.log(programme);  // output: Pharmacy

    We can also apply object destructuring to destructure object parameters in function headers:

    JavaScript
    function show({ id, name, email }) {
          console.log(name);
          console.log(email);
    }
    
    
    show({ id: 1, name: 'Emmanuel', email: 'emma@mail.com' });

    Other operations that can be performed with Javascript object destructuring include the use of spread syntax to extend, merge, clone, and update objects, as well as pack multiple properties into a single variable with the rest operator.

    We can also apply object destructuring technique to destructure nested objects.

  • Array Destructuring in Javascript

    Javascript array destructuring, introduced in ES6 Javascript, is a programming technque that makes it possible to extract values from data such as arrays, objects, and strings, into variables. It provides a very concise way to extract multiple values from an array into variables and avoids repetitive array indexing.

    Given an array data, we can extract the values of specific array elements and ignore those that we do not need. In addition, we can use array destructuring to easily swap the values of two variables without the need to declare a temporal variable.

    In this post, we will demystify Javascript array destructuring and understand how it simplifies array operations such as array concatenation, array cloning, extraction of values from nested arrays, etc.

    Destructuring Assignment

    In javascript, destructuring assignment is an expression that makes it possible to selectively extract all or part of an array or object data and store them in specific variables in a single statement. In other words, destructuring assignment is an expression that allows for the unpacking of array and object data into variables.

    Destructuring assignment was introduced in ES6 (ECMAScript 2015) Javascript. Before its introduction, we normally access the elements of an array by typing the name of the array followed by the index of the element enclosed in square brackets.

    For example, let’s suppose we have an array of programming languages, as shown below:

    JavaScript
    let languages = ['Javascript', 'PHP', 'Python', 'Java'];

    In the pre-ES6 era, if we need to extract the array elements into variables, we will need to use array indexing to get the specific elements and store them in variables:

    JavaScript
    let languages = ['Javascript', 'PHP', 'Python', 'Java'];
    
    // extract array elements into defined variables
    let var1 = languages[0];  // will contain Javascript
    let var2 = languages[1];  // will contain PHP
    let var3 = languages[2];  // will contain Python
    let var4 = languages[2];  // will contain Java
    
    // log values
    console.log(var1, var2, var3, var4);  // output: Javascript PHP Python Java

    In our attempts to extract the array elements into variables, we used array indexing to extract the array items from the array and saved them in variables: var1, var2, var3, and var4. This technique is very common and works very well. However, ES6 Javascript has a better way of handling such assignments.

    The ES6 Javascript introduces a very compact way of assigning array elements to variables without repetitive array indexing. With this approach, we can extract array elements and assign them to variables, all in just a single expression or statement. Consider the following code listing:

    JavaScript
    let languages = ['Javascript', 'PHP', 'Python', 'Java'];
    
    // extract array elements into defined variables
    let [var1, var2, var3, var4] = languages;
    
    // log values
    console.log(var1, var2, var3, var4);  // output: Javascript PHP Python Java

    The expression [var1, var2, var3, var4] = languages on line 4 is called destructuring assignment. The expression declares multiple variables and maps them to the elements in the languages array in the same order.

    Think of the destructuring assignment as simultaneously declaring multiple variables, var1, var2, var3, and var4, and then unpacking the elements of the languages array into these variables. During unpacking, the values of the languages array elements are extracted into the declaration variables in the same array indexing order.

    For example, since var1 has index 0, it will correspond with languages[0], and therefore its value will be Javascript. Since var2 has index 1, it will correspond with languages[1] and therefore its value will be PHP, and var3, with index 2, will have the value Python, and so on..

    Now compare listing 3 to listing 2. If you are not already familiar with destructuring assignment in Javascript, then I bet you are beginning to love this programming construct, considering its compactness and the cleaner code it offers.

    But that is not all. There are a lot that can be done with array destructuring, and we will cover them in this post. For example, destructuring assignment enables us to selectively extract specific elements of an array into variables and ignore those we do not need.

    Destructuring Assignment Syntax

    Before we dive deeper into array destructuring, it is important to understand the basic syntax for destructuring assignment. We will consider this basic syntax for both arrays and objects. The discussion on object destructuring is extensiviely discussed in a separate post, and can be accessed at Javascript Object Destructuring.

    When extracting data from an array, the destructuring declaration variables need to be enclosed in square brackets: []. For example:

    JavaScript
    // In array destructuring, declaration
    // variables are enclosed in brackets: []
    let [var1, var2, var3] = ArrayData;

    The names enclosed in brackets are variables into which we want to store the extracted values from the array. Therefore, these names must conform to Javacript variable naming considerations.

    If we need to destructure values from an object, we use braces (square brackets), {}, to enclose the declaration variables into which we store the extracted values:

    JavaScript
    // In object destructuring, declaration
    // variables are enclosed in braces: {}
    let {var1, var2, var3} = ObjectData;

    From the discussions so far, we can identify that the general syntax for destructuring arrays and objects are very similar, as shown below:

    JavaScript
    let [] = ArrayData;
    let {} = ObjectData;

    Thus, when destructuring an array, we use brackets, [] to enclose the variables in the destructuring declaration expression. For object destructuring, we use braces, {}, to enclose the declaration variables.

    In the discussions that follow, we will delve deeper into array destructuring. We consider javascript object destructuring in a separate post.

    Array Destructuring

    The ES6 Specification introduces array destructuring assignment through which we can assign the elements of an array to multiple variables in just a single statement. This eleminates the need to index the array elements one at a time in order to get the element values we need, as done in listing 2.

    For example, given that we have an array of countries as shown below:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    const var1 = countries[0];
    const var2 = countries[1];
    const var3 = countries[2];
    const var4 = countries[3];

    Using array destructuring statement, we can rewrite listing 7 as the following:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    // extract individual array elements into variables
    const [var1, var2, var3, var4] = countries;

    Line 5 is an array destructuring statement. This statement declares the variables var1, var2, var3, and var4, and then initializes them to the elements of the initializer array, countries. The elements in the initializer array are assigned to the variables by mapping each variable in the destructuring declaration to an element in the initializer array in the same order.

    To understand this better, think of the destructuring declaration [var1, var2, var3, var4] as an array. Then variable var1 has index 0 and so maps to countries[0], var2 has index 1 and so maps to countries[1], var3 has index 2 and so maps to countries[2], and so on; These mappings and extraction of values are done by Javascript so that we do not need to assign the elements in the countries array one at a time with array indexing. With destructuring assignment, we are able to do all at once.

    Separating Variable Declarations from Assignment

    In destructuring assignment, we are not mandated to always declare new variables for the assignment. Existing variables can be used in the destructuring declaration to assign values from the initializer array. An example is shown in the following code listing:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    // declare target variables
    let var1, var2, var3, var4;
    
    // extract individual array elements into variables
    [var1, var2, var3, var4] = countries;
    
    // log the values
    console.log(var1);  // var1: Ghana
    console.log(var2);  // var2: Canada
    console.log(var3);  // var3: United States
    console.log(var4);  // var4: Germany

    The variables var1, var2, var3, and var4 are declared separately on line 5. Then on line 8, we use array destructuring statement to assign the initializer array elements to the already declared variables.

    As can be seen, we group the variables that we want to assign values in brackets, and then assign them to the initializer array. Javascript will automatically map the variables to the initializer array in order, and extract the values from the initializer array elements into the variables.

    Variables Unmapped to Initializer Array Elements Are undefined

    We have indicated that in an array destructuring statement, Javascript maps each item in the destructuring declaration array to items in the initializer array. However, if an item in the declaration array does not map to an item in the initializer array, then the value of that variable is undefined. This happens when the number of variables in the destructuring declaration array is more than the number of items in the initializer array.

    Consider the following code listing:

    JavaScript
    // array declaration
    const languages = ['PHP', 'Java', 'Javascript'];
    
    // destructure the array into variables
    const [var1, var2, var3, var4] = languages;
    
    // log value of var4
    console.log(var4);  // output: undefined

    Observe that there are four destructuring declaration variables: var1, var2, var3, and var4. However, The initializer array, languages, has only three items.

    In destructuring the initializer array, var1 maps to languages[0], var2 maps to languages[1], and var3 maps to languages[2]. However, var4 does not map to any element in the languages array. The result is that var4 is declared as a variable without an initializing value, similar to a variable declaration like the following which will have undefined as its value:

    JavaScript
    // declaration of variable without initializing value
    const var4;

    Thus, the value of a variable in the declaration array which is not mapped to an element in the initializer array will be undefined.

    Setting Default Values for Variables

    When destructuring an array into variables, we can set default values to be used when the value of a variable is undefined.

    For example, in the previous section, we indicated that if a variable in the destructuring declaration array is not mapped to an element in the initializer array, then its value will be undefined.

    If we like, we can provide a default value which should be used if the value of a variable is undefined after the array destructuring. To do this, we assign the default value to the variable in the declaration array:

    JavaScript
    // array declaration
    const languages = ['PHP', 'Java', 'Javascript'];
    
    // destructure the array into variables
    const [var1, var2, var3, var4 = 'Python'] = languages;
    
    // log value of var4
    console.log(var4);  // output: Python

    In the destructuring of the array on line 5, var4 does not map to any item in the languages array and therefore should have its value to be undefined. However, on this same line, we assign a default value to var4. The default value will be used if the value of var4 is undefined after destructuring.

    Default values can be assigned to multiple variables in the destructuring declaration array. However, these default values are only used if the value extracted from the initializer array is undefined or when the variable has no mapping in the initializer array. For example:

    JavaScript
    // array declaration
    const languages = ['PHP', 'Java', 'Javascript'];
    
    // destructuring declaration array
    const [var1 = 'C#', var2 = 'C++', var3 = 'C', var4 = 'Python'] = languages;
    
    // log values
    console.log(var1);  // output: PHP
    console.log(var2);  // output: Java
    console.log(var3);  // output: Javascript
    console.log(var4);  // output: Python

    On line 5, we have set default values for all the variables in the destructuring declaration array. When mapped to the initializer array, we can observe that the values that will be extracted from the languages array for var1, var2, and var3 are not undefined. Therefore, the corresponding values in the initializer array are used rather than the default values. var4 which is not mapped to an element in the initializer array, and expected to be undefined, will use the default value set in the declaration array.

    Let’s deliberately change the vaue of the first element in languages array to undefined:

    JavaScript
    // array declaration
    const languages = [undefined, 'Java', 'Javascript'];
    
    // destructuing declaration array
    const [var1 = 'C#', var2 = 'C++', var3 = 'C', var4 = 'Python'] = languages;
    
    // log value of rust variable
    console.log(var1);  // output: C#
    console.log(var2);  // output: Java
    console.log(var3);  // output: Javascript
    console.log(var4);  // output: Python

    After re-running the code, we should expect the value of var1 to be the default value set in the destructuring declaration array. This is because var1, which maps to languages[0], will have its value being undefined, and therefore the default value instead gets assigned to var1.

    Skipping to Selectively Extract Values

    Sometimes, we may not need all the data that is contained in a given array. We may need the value of only one or a few of the array elements. If the array elements that we need aren’t contiguous, especially not at the beginning, then that will imply that we skip those that we do not need and extract those that we need.

    Consider an initial array data below:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];

    Let’s suppose we are only interested in getting the values of the second, third and fourth array elements. In the pre-ES6 era, we will use array indexing to access the values, such as in the following code listing:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    const var1 = countries[1];
    const var2 = countries[2];
    const var3 = countries[3];

    With ES6 array destructuring, we can skip values by omitting variables names in the declaration array. To skip the first array item in the initializer array, we will omit the variable name from the declaration array. The comma is however important. It is an indication that we intend to ignore extraction of the value at that index.

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    // ignore first item, and extract the rest
    const [, var2, var3, var4] = countries;

    Take a careful look at line 5. In the destructuring declaration array, there is no variable name specified before the first comma. We can identify that the position of the first item in the declaration array is blank, signalling that we intend to skip the mapping of the first array element in the initializer array.

    Let’s again suppose we intend to extract only the second and fourth elements from countries array. Since we want to ignore the first array element (index 0), and the third array element (index 2), we will not provide any variable names at the indexes we intend to ignore in the declaration array:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    // ignore first and third items, and extract second and fourth
    const [, var2, , var4] = countries;
    
    // log values
    console.log(var2);  // output: Canada
    console.log(var4);  // output: Germany

    Now what if we intend to extract only the last element from countries? Again, we will not provide variable names at the indexes we intend to ignore:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Canada', 'United States', 'Germany'];
    
    // ignore first three items, and extract the last
    const [, , , var4] = countries;
    
    // log values
    console.log(var4);  // output: Germany

    On line 5, the first comma skips the first element in countries, the second comma skips the second element in countries, and the third comma skips the third element in countries. var4, then, maps to countries[3].

    Swapping Values of Two Variables

    Javascript array destructuring construct enables us to easily swap the values of two variables without the need to explicitly declare a third variable.

    Before array destructuring was introduced in ES6 Javascript, you will declare a third variable as a temporary storage space in order to swap the values of two variables. The following code listing is a reminder of how it used to be done:

    JavaScript
    // variables declaration
    let var1 = 50;
    let var2 = 80;
    
    // we need a temporal variable to store one of them
    const temp = var1;
    
    // swap values of var1 and var2
    var1 = var2;
    var2 = temp;
    
    // log variables
    console.log(`var1=${var1}`);    // output: var1=80
    console.log(`var2=${var2}`);    // output: var2=50

    With array destructuring, we can swap the values of two variables in just a single statement. This is done by changing the order of the variables in the declaration array and the initializer array. The following code listing is an example:

    JavaScript
    // variables declaration
    let var1 = 50;
    let var2 = 80;
    
    // swap values of var1 and var2, 
    [var1, var2] = [var2, var1];
    
    // log variables
    console.log(`var1=${var1}`);    // output: var1=80
    console.log(`var2=${var2}`);    // output: var2=50

    Observe line 9 carefully. We use the two variables that we want to swap their values in the declaration array and the initializer array. You should notice that the order of the declaration array items and the initializer array items are in opposite order.

    In the declaration array, var1 is declared as the first item followed by var2. Then in the initializer array, var2 appears before var1. This means that we have interchanged the order of var1 and var2 in the declaration array and the initializer array. In fact, this is the only single statement we need in order to swap their values, nothing more. After running the code, you should see that the values of these two variables have been swapped.

    Using Rest Syntax to Assign Remaining Values

    Sometimes, we may need to extract specific elements into separate variables and then extract the remaining elements into some other variable. We can achieve this by using the rest operator introduced in ES6 javascript.

    The rest operator is a three consecutive dots, (...), operator, which precedes a variable in a destructuring declaration. It used to pack the rest, or remaining values of an iterable, such as an array, into the variable preceded with the rest operator.

    Consider the following code listing:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Spain', 'France', 'Germany'];
    
    // extract first item into africa, then pack the remaining into europe
    const [africa, ...europe] = countries;
    
    // log values
    console.log(africa);    // output: Ghana
    console.log(europe);   // output: ['Spain', 'France', 'Germany']

    On line 5, the variable africa maps to countries[0]. The rest operator, ..., preceding the variable europe, that is, ...europe, means that the rest of the elements, starting from the index mapping of where the rest operator is used, should all be packed into the variable preceded with the rest (...) operator.

    Thus, after destructuring, europe will be an array containing the rest of the elements of the countries, starting from the indexing mapping of europe (countries[1]) to the initializer array, countries.

    Considering another example, we can decide to extract the first two items and pack the rest in a different variable:

    JavaScript
    // array declaration
    const countries = ['Ghana', 'Spain', 'France', 'Germany'];
    
    // extract first and second, then pack the rest in different variable
    const [ghana, spain, ...rest] = countries;
    
    // log values
    console.log(ghana);  // output: Ghana
    console.log(spain);  // output: Spain
    console.log(rest);   // output: ['France', 'Germany']

    Using Spread Syntax to Expand Array Elements

    The Javascript spread syntax is an expression that expands an iterable data, such as array, string, or object, into individual elements separated by commas. Like the rest syntax, the spread syntax also uses three consecutive dots, (...), preceding an iterable variable to expand the iterable data into individual elements separated with commas.

    For example, to spread the elements of an array, we precede the array variable name with the spread operator, (...). An example is shown in the following code listing:

    JavaScript
    // array definition
    const data = [1, 2, 3, 4, 5];
    
    // spread the array items
    console.log(...data);

    On line 5, we precede the array variable name, data, with three consecutive dots (...). This expands the array into individual items separated by commas. The effect is that line 5 expands to the following call:

    JavaScript
    // ...data expands the array elements, separated by commas
    console.log(1, 2, 3, 4, 5);

    It is important to understand that passing an array as argument to a function call and spreading an array as argument to a function call are not the same. Hence, in the following code listing, the calls to the log method of the console object are not the same:

    JavaScript
    // array definition
    const data = [1, 2, 3, 4, 5];
    
    console.log(data);      // output: [1, 2, 3, 4, 5]
    console.log(...data);   // output: 1 2 3 4 5

    On line 4, we pass the array as argument to the log method without preceding it with the spread operator. In this case, we are passing only one argument to the log method. This, logs data as array in the console.

    On line 5, we precede the array with the spread operator. This expands the array by spreading and separating the individual array elements with commas. Thus, after spreading the array, we are actually passing five arguments to the log method.

    With the basic understanding gained so far, we should look at other code logic in which the spread syntax can be used.

    Cloning An Array Using Spread Syntax

    When we assign an array to another variable, the two variables become arrays pointing to the same array data. For example:

    JavaScript
    // initialise array
    let A = [1, 2, 3, 4];
    
    // assign A to new variable B
    let B = A;
    
    // log values
    console.log('A = ', A);
    console.log('B = ', B);

    As expected, both A and B will have the same values.

    However, if we modify an element in A, it will affect the values in B:

    JavaScript
    // initialise array
    let A = [1, 2, 3, 4];
    
    // assign A to new variable B
    let B = A;
    
    // modify fourth element in A
    A[3] = 10;
    
    // log values
    console.log('A = ', A);
    console.log('B = ', B);

    On line 8, we modify the value of the fourth element in A. However, this also updates the value of the fourth element in B, as shown below:

    This happens because A and B have the same reference to the array data.

    We can use the ES6 Javascript spread syntax to easily clone the values of one array into another so that they will not reference the same array data. Rather than assign A to B, as we did on line 5 in listing 29, we will spread the elements of A to initialize a new array which will be assigned to B:

    JavaScript
    // initialise array
    let A = [1, 2, 3, 4];
    
    // assign A to new variable B
    let B = [...A];
    
    // modify an element in A
    A[3] = 10;
    
    // log values
    console.log('A = ', A);
    console.log('B = ', B);

    On line 5, we set B to a new array that we initialize by spreading the elements A within brackets, []. This makes A and B have different references to array data, although they have the same values. An update to array A on line 8 does not affect array B, as shown in the following log output:

    Thus, array A is a clone of array B. It should however be noted that this approach only creates a shallow copy of the source array.

    Concatenating Arrays

    The spread syntax can also be used to concatenate two arrays. Before ES6, it was common to use the concat method of an array to concatenate two arrays. With ES6, we can use the spread syntax to achieve same.

    Suppose we have two arrays, given in the following code listing:

    JavaScript
    // array declarations
    const A = [1, 2, 3, 4];
    const B = [5, 6, 7, 8];
    
    // concatenate A and B
    const C = [...A, ...B];
    
    console.log(C);  // output: [1, 2, 3, 4, 5, 6, 7, 8]

    On line 6, we use the spread syntax to expand arrays A and B. The effect is that a new array is initialized with its content being the values of both arrays A and B, and assign the generated array to C.

    Destructuring Array in Function Arguments

    If a function accepts arguments in its call, and we have the values to be passed to the function in an array, there is no need to use array indexing to set the argument values. If the ordering of the array elements correctly map to the function parameters, we can use the spread syntax to spread the argument values.

    For example, suppose we have the following function which has three parameters:

    JavaScript
    // function definition
    function getVolume(width, length, height) {
         return width * length * height;
    }

    Suppose also that we have the values to be passed to the function in an array with correct mapping of the array elements to the parameters:

    JavaScript
    // object shape data [width, length, height]
    const data = [3, 5, 4];

    Without array destructuring, we will pass the values to the function by indexing the array:

    JavaScript
    // object shape data [width, length, height]
    const data = [3, 5, 4];
    
    // calculate object volume
    let volume = gettVolume(data[0], data[1], data[2]);

    With array destructuring, we can spread the array elements when calling the function:

    JavaScript
    // object shape data [width, length, height]
    const data = [3, 5, 4];
    
    // calculate object volume
    let volume = getVolume(...data);

    As can be seen, using the spread syntax involves less typing when compared to accessing the values with array indexing.

    Destructuring Array in Function Parameters

    Traditionally, when an array is passed as argument to a function, we access the values of the array in the function by using array indexing to retrieve the individual array elements. For example, suppose we have a function that calculates the volume of an object based on values passed to it as array:

    JavaScript
    function getVolume(arrData) {
        const width = arrData[0];
        const length = arrData[1];
        const height = arrData[2];
        
        // calculate and return volume
        return width * length * height;
    }

    Rather than use array indexing to retrieve the values into variables, we can use ES6 array destructuring construct to destructure the array parameter into individual variables without the need to access the values with array indexing. But before we see how this is done, let’s understand what happens when we pass a value as argument to a function.

    Suppose we have a function that accepts a value in its call, such as the following:

    JavaScript
    function someFunction(PARAM) {
        return PARAM * 2;
    }

    As we know, we will call the function by passing a value to it as an argument:

    JavaScript
    // call the function
    someFunction(ARG);

    When the function is called, the value of ARG is assigned to the variable PARAM declared in the function’s signature. This implies that the function call leads to assignment of ARG TO PARAM:

    JavaScript
    PARAM = ARG

    Now suppose ARG is an array, [1, 2, 3], and we pass it as argument to the function, this results in the assignment of the array to PARAM declared in the function signature:

    JavaScript
    PARAM = [1, 2, 3];

    But in our early discussions on array destructuring, we indicated that we can destructure an array into different variables, such as the following:

    JavaScript
    [width, length, height] = [1, 2, 3];

    With this understanding, we can rewrite arrData in getVolume() function by replacing it with destructuring declaration variables:

    JavaScript
    function getVolume([width, length, height]) {
        // calculate and return volume
        return width * length * height;
    }

    Thus, if a function accepts an array as an argument, then we can destructure the array parameter into declaration variables directly in the function header without the need for array indexing to retrieve the individual values into separately declared variables.

    Destructuring Nested Arrays

    We can apply our understanding of array destructuring so far to destructure an array within an array. As we will soon see, that is a very easy task, though it may seem daunting at first encounter. We will take step by step approach to better your understanding of nested array destructuring.

    First, let’s assume we have the following nested array:

    JavaScript
    const numbers = [0, [1, 2, 3]];

    We can identify that the array numbers has two elements. The first element, numbers[0], has the value 0, and the second element, numbers[1] has an array, which is [1, 2, 3].

    Our task is to extract all the numerical values into variables. We will name the variables after the number we want to retrieve, such as one, two, three, etc. Let’s see how we can do this with array destructuring.

    First, let’s observe again that the numbers array has two elements. Suppose we destructure the array into two variables, A and B:

    JavaScript
    const numbers = [0, [1, 2, 3]];
    
    // destructure into variables
    const [A, B] = numbers;

    In this code listing, A maps to numbers[0] which has the value 0, and B maps to numbers[1] which is an array: [1, 2, 3]. Since B is an array, we can further destructure it into additional declaration variables. But since we already know that A has the value 0, lets replace it with variable name zero which is its numerical value that we extract, and then continue from there:

    JavaScript
    const numbers = [0, [1, 2, 3]];
    
    // destructure into variables
    const [zero, B] = numbers;

    Recall that B maps to numbers[1], which is an array: [1, 2, 3]:

    JavaScript
    B = [1, 2, 3];

    We can now destructure B into additional declaration variables with the names of the values in the array:

    JavaScript
    const [one, two, three] = B

    We can replace B with the declaration variables: [one, two, three]. Hence, on line 4 in listing 45, we can substitute [one, two, three] in place of B:

    JavaScript
    const numbers = [0, [1, 2, 3]];
    
    // destructure into variables
    const [zero, [one, two, three]] = numbers;
    
    // log the values
    console.log(zero, one, two three);  // output: 0 1 2 3

    Using array destructuring syntax, we have been able to destructure both the outer array and the inner array of numbers into distinct variables.

    Let’s further test our understanding of nested array destructuring with another challenge by updating the numbers array to the following:

    JavaScript
    const numbers = [0, [1, 2, [3, 4]]];

    We can identify that the numbers array has two elements. The first item, numbers[0], has the value 0. The second item, numbers[1], has its value to be an array, which is [1, 2, [3, 4]]. Further, we can identify that numbers[1][2] also contains an array which is [3, 4].

    Going by the approach discussed earlier, we can destructure the values in the numbers array into variables as shown in the following code listing:

    JavaScript
    const numbers = [0, [1, 2, [3, 4]]];
    
    // destructure into variables
    const [zero, [one, two, [three, four]]] = numbers;
    
    // log the values
    console.log(zero, one, two, three, four);  // output: 0 1 2 3 4

    Javascript array destructuring syntax is not only elegant but also a time-saving approach. In just a single statement, we have been able to extract values of array elements, event nested ones, into distinct variables.

    Wrapping Up

    Javascript array destructuring is a programme technique introduced in ES6 Javascript which makes it possible to unpack an array and store the individual elements into variables. This can be done in just a single statement without the need to index the array to extract the values.

    For example, given the following array:

    JavaScript
    // array declaration
    const numbers = [1, 2, 3, 4];

    We can use array destructuring assignment syntax to extract all the values from the array in just a single statement:

    JavaScript
    // array declaration
    const numbers = [1, 2, 3, 4];
    
    // extract values
    const [one, two, three, four] = numbers;

    The declaration assignment syntax on line 5 declares variables one, two, three, and four, and then maps each variable to the numbers array in order. In this case, one maps to numbers[0], two maps to numbers[1], three maps to numbers[2], and four maps to numbers[3].

    If we do not need all the values from the initializer array, then we can skip specific values in the initializer array by ommiting variable names that should map to the elements we want to skip. For example, if we want to skip the first element in numbers, then we will not provide any variable name in the declaration array:

    JavaScript
    // array declaration
    const numbers = [1, 2, 3, 4];
    
    // ignore first element
    const [, two, three, four] = numbers;

    Observe that there is variable name before the first comma, indicating that we want to skip this mapping into the numbers array.

    If we want to skip the first and second elements, then following will do:

    JavaScript
    // array declaration
    const numbers = [1, 2, 3, 4];
    
    // ignore first and second elements
    const [, , three, four] = numbers;

    Sometimes, we may choose to extract specific value and pack the remaining ones into another variable. We can pack the remaining elements into a variable using the rest operator, (...). For example:

    JavaScript
    // array declaration
    const numbers = [1, 2, 3, 4];
    
    // extract first element, and pack the remaining into different variable
    const [one, ...others] = numbers;
    
    console.log(one);   // output: 1
    console.log(rest);  // output: [2, 3, 4]

    In this code listing, one maps to numbers[0]. The rest operator in frot of others implies that starting from the current mapping, the remaining ones should all be packed into others variable.

    We can even use array destructuring syntax to swap values of two variables. For example:

    JavaScript
    let first = 10, second = 20;
    
    // swap their values
    [first, second] = [second, first];
    
    // log the values
    console.log(first);  // output: 20
    console.log(second); // output: 10

    We can extend array destructuring syntax to destructure array parameters in functions:

    JavaScript
    function getTotal([var1, var2, var3]) {
        return var1 + var2 + var3;
    }
    
    const data = [10, 20, 30];
    
    // get total
    let total = getTotal(data);

    Other operations that can performed with Javascript array destructuring include concatenation and shallow cloning of arrays. It can also be used to simplify extraction of values from nested arrays.

  • Understanding React useRef Hook

    React useRef is a hook that allows a function component to create a reference to a value and track its state between component re-renders. This is made possible because a ref object is able to persist its value so that it will not lose track of the value it references during component re-render. The reference can also be used to update the value that is being tracked.

    In addition to preserving values between re-renders, the useRef hook is also used to create references to DOM elements and access their values or perform manipulations on them. For example, we can create reference to form input elements and extract their values after the user submits the form.

    In contrast to state variables, ref objects are mutable. This implies that we can directly modify the values tracked by a ref object without the need for a special function. Updates to ref values do not trigger a re-render of the containing function component, making them very efficient.

    In this post, we will demistify React useRef hook with clear examples to better understand its capabilities and usage.

    What is the use of useRef Hook?

    Basically, React useRef hook is used to create a reference to a value and preserve its state so that the value will not be lost when the function component re-renders. Additionally, it can be used to keep reference to a DOM element and retrieve or manipulate its properties such as content, appearance, etc.

    To better understand what we mean by preserving the state of a variable, lets’ look at the general design of a javascript function.

    Traditionally, creating a value in a javascript function involves a variable declaration statement such as the following:

    JavaScript
    function logCallsCount() {
        // initialise counter variable
        let callsCount = 0;
    }

    Any updates to values initialised within the function, such as the value of callsCount in listing 1, are retained as long as the function is still running. In other words, the function will remember the state of all variables declared within it as long as it has not exited:

    Consider the following code listing:

    JavaScript
    function logCallsCount() {
        // initialise counter variable
        let callsCount = 0;
        console.log(`Calls count is: ${count}`)
        
        // update calls counter
        callsCount += 1;
        console.log(`Calls count is: ${count}`)
    }
    
    // call the function to log calls count
    logCallsCount();

    A single call to logCallsCount() on line 12 will produce the following in the console, which is exactly what we expect:

    A general design of javascript functions, and in most programming languages, is that when a function completes its task and exits, the values of its variables are lost. Subsequent function calls will not remember the values of the variables in the previous calls. The values of the variables are reset when the function is called again and begins execution.

    The following code listing calls logCallsCount() twice.

    JavaScript
    // call the function to log calls count
    logCallsCount();
    logCallsCount();

    The output of the code is shown below:

    In the first call to logCallsCount(), the value of callsCount was 1 before the function exited. When the second call to logCallsCount() begins execution, it does not remember that the value of callsCount was 1 when the first call to logCallsCount() exited. Therefore, callsCount is reset to the value 0 before being incremented by 1. Hence, in each call to logCallsCount(), the same values are logged to the console.

    In modern React applications development, function components are widely used. When part of the application UI needs to be updated, React calls the function component to refresh and update the UI, a process commonly referred to as re-render. Thus, a function component can be called multiple times to perform UI update. This re-render of components causes local variables to be reset to initial values, losing any values they contained before the re-render, as demonstrated earlier.

    To address this challenge, React uses hooks such as useState, useReducer, and useRef hooks to create a reference to a value and track its state so that its value will not be lost when the function component re-renders. In this post, we will focus on how to preserve values and access DOM elements with useRef hook. The section on understanding useState hook tackles how to preserve and update values with useState hook.

    Creating a Reference with useRef

    To create a reference to a value that can be preserved between component re-renders, or access a DOM element, we first need to import useRef from React:

    JavaScript
    import { useRef } from 'react';

    Once imported, we can create a reference variable by calling useRef():

    JavaScript
    let clicksCount = useRef();

    In the above code listing, we declare clicksCount as a reference variable and initialize it with a call to useRef(). As indicated earlier, this gives clicksCount the ability to preserve the value it references so that when the function component in which it is defined re-renders, clicksCount can remember its state from the previous render.

    useRef is a hook and therefore its usage must comply with the rules of hooks in React. For example, initialization of a ref object with useRef() must be done at the beginning of the body of the function component.

    Mostly, it is appropriate to set an initial value when we declare a variable. For example:

    JavaScript
    let clicksCount = 0;

    We can similarly set an initial value when we declare a ref variable in React application. To do this, we pass the intial value as an argument to useRef():

    JavaScript
    let clicksCount = useRef(0);

    Just as we can assign values of any javascript type to a variable, we can also assign any value such as number, string, array, object, etc by passing it to useRef():

    JavaScript
    let numberRef = useRef(20);
    let stringRef = useRef('Hello React');
    let arrayRef  = useRef( ['React', 'Javascript'] );
    let objectref = useRef( {library: 'React', language: 'Javascript'} );
    let undefinedRef = useRef(undefined);
    let nullRef = useRef(null);

    If we do not pass an initial value to useRef(), then the value of the reference variable will be undefined.

    Now that we know how to initialize a reference variable, let’s compare a regular javascript variable declaration with a React reference variable declaration:

    JavaScript
    // declaration of regular javascript variable
    let callsCount = 5;
    
    // declaration of React reference variable
    let clicksCount = useRef(5);

    As can be seen, creating a ref variable in React isn’t much of a pain. We only need to set the ref variable to useRef(), while passing an optional initial value as argument to useRef(). Other than this, the two declarations are syntactically close.

    Ref Variables are Objects

    A ref variable is actually an object which contains a single property named current. This is because a call to useRef() returns an object, with the optional initial value set as value to the current property:

    JavaScript
    { current: initialValue }

    Whereas we can access the value of regular javascript variable directly, we can only access the value of a ref variable through its current property:

    JavaScript
    // declaration of a ref variable
    let clicksCount = useRef(0);
    
    // access the value tracked by the ref variable
    console.log(clicksCount.current);

    As can be seen, we are able to access the value referenced by the ref object through the current property.

    Ref Objects are Mutable

    Unlike state variables initialized with useState() which must be treated as immutable, ref variables are mutable. This implies that we can directly modify a ref object without the need for a special function, or set them to a newly initialized object data. Therefore, we can update the value of the ref object by directly setting the new value to the current property:

    JavaScript
    import React, { useRef } from 'react';
    
    const ClicksCounter = () => {
        // declaration of a ref variable
        let clicksCount = useRef(0);
    
        // update the value of the ref object
        clicksCount.current = 5;
        clicksCount.current += 1;
    
        // access the value tracked by the ref variable
        console.log(clicksCount.current);  // will output 6
        
        return <></>
    }
    
    export default ClicksCounter;

    Observe that on lines 8 and 9, we modified the value referenced by the ref object through the current property.

    Ref Updates Do Not Re-render

    When the value referenced by a ref object is updated, React does not call the function component to re-render and update the application UI. Due to this, a ref object value is not suitable in UI code. This is because the application view will not be updated to reflect current values of ref objects until an update to a state variable has been done to trigger a re-render. Thus, ref objects are not expected to be used in JSX code.

    In some cases, using ref objects in JSX may work, but this may lead to unexpected application behaviour. For best practices, we should avoid ref objects in JSX. If we need values that need to be preserved and also be used in the UI, then we need to initialize the values with useState().

    Preserving Values with useRef

    As indicated earlier, one of the uses of useRef hook is to preserve some values so that the values can be recoverd when the function component re-renders. It is ideal for storing values that should not trigger a re-render when the values are updated. This is in contrast to state variables which trigger a re-render when updated.

    Consider the following code listing which defines a ref variable and a regular javascript variable. The code demonstrates how a regular javascript variable differs from a ref variable.

    To confirm that a ref object persists its value when a component re-renders, we will introduce a state variable which wie will intentionally update in order to trigger a component re-render.

    JavaScript
    import React, { useRef, useState } from "react";
    
    const ClicksCounter = () => {
        const [count, setCount] = useState(0);
        let refCounter = useRef(0);     // ref variable
        let nonRefCounter = 0;          // regular variable
    
        console.log(`Ref count is: ${refCounter.current}`);
        console.log(`Non-ref count is: ${nonRefCounter}`);
    
        const onBtnClick = () => {
            refCounter.current += 1;
            nonRefCounter += 1;
    
            // To trigger a re-render, we intentionally
            // update a state variable
            setCount(refCounter.current);
        }
    
        return (
            <div style={{width: '50%', margin: '20px auto'}}>
                <button onClick={onBtnClick}>Click Me</button>
            </div>
        )
    }
    
    export default ClicksCounter;

    On lines 5 and 6, we declare a ref variable and a regular javascript variable respectively. The intent is to demonstrate that a regular javascript variable will lose track of its value whiles a ref variable will remember its previous value during component re-render.

    When the button is clicked, we update both refCounter and nonRefCounter in the onBtnClick event handler. To confirm that a ref object persists its value when a component re-render occurs, we intentionally update the state variable, in this case count, in order to trigger a re-render. The updated values are then logged to the console. We can see the result below:

    As can be seen, React calls the ClicksCounter component to re-render when the button is clicked. That is because we perform an update to a state variable in the event handler.

    You should notice from the log that refCounter persists its previous value and uses it during re-render. It is therefore able to increment its value when the button is clicked. However, nonRefCounter, which is a regular javascript variable, is not able to persist its value and recall it during re-render. Hence, nonRefCounter always resets to the initial value, outputting the value 0.

    We have said that an update to the value referenced by a ref object does not trigger a re-render. In the previous example, a re-render of the component occurs because we intentionally did so by updating a state variable.

    To prove that an update to a ref object does not cause a component re-render, remove or comment out line 17 in listing 13. This time, there will be no log output when the button is clicked. The ClicksCounter component isn’t called to re-render, hence line 8 and line 9 aren’t executed again.

    Accessing DOM Elements

    At this point, we have a very firm understanding of the use of useRef hook in React applications development. It is used to store a value, or object, so that the value it references will not be lost when a function component in which it is defined re-renders.

    In addition to persisting values between component re-renders, React useRef hook can be used to create a reference to a DOM element, eliminating the need for javascript libraries such as jQuery. Although it is possible to use jQuery in React applications, if its requirement is basically for the purpose of referencing and or manipulating the DOM, then jQuery isn’t needed. React useRef hook does the job well. It can be used to reference DOM elements and manipulate their contents.

    Let’s consider a very simple React function component that displays an input control and a button: We will enter a name in the input control and click on the button.

    JavaScript
    import React, { useRef } from "react";
    
    const MyName = () => {
        return (
            <div style={{width: '60%', margin: '20px auto', textAlign: 'center'}}>
                <input type='text' placeholder="Enter your name" />
                <button>Submit</button>
            </div>
        )
    }
    
    export default MyName;

    A preview of the MyName component, when rendered in the browser, should look similar to the following:

    What we intend to do is to get and display the text entry after the Submit button is clicked. To do this, we will need a way to reference the input control so that we can retrieve its value after the button is clicked.

    If you have worked with the DOM API before, then you know that we will need to get a reference to the input control. In the non-react way, it is done by specifying an id attribute on the element:

    HTML
    <input type='text' id='myName' />

    To get a reference to the input control dynamically, we call document.getElementById(), or use jQuery javascript library to return it:

    JavaScript
    // get reference to the input control using DOM api
    let myNameInput = document.getElementById('myName');
    
    // get reference to the input control using jQuery
    let myNameInput2 = $('#myName');

    Although these methods will work in React, they require that we run codes like these in an effect function using React useEffect hook. For detailed coverage of React useEffect hook, read on the concise discussion on understanding react useEffect hook.

    To reference a DOM element in the React way, we need to add ref attribute to the element of concern, and then initialize a ref object with useRef(). Rather than specify an id attribute on the DOM element, we will specify a ref attribute and set its value to the ref object that we initialized.

    JavaScript
    import React, { useRef } from "react";
    
    const MyName = () => {
        // initialize a ref object with useRef
        const myNameRef = useRef();
        
        return (
            <div style={{width: '60%', margin: '20px auto', textAlign: 'center'}}>
                <input type='text' placeholder="Enter your name" ref={myNameRef} />
                <button>Submit</button>
            </div>
        )
    }
    
    export default MyName;

    As can be seen, we initialize ref variable, myNameRef, on line 5 with useRef(). Then on line 9, we have specified a ref attribute rather than id attribute on the input element. We have also set the value of the ref attribute to myNameRef which we initialized on line 5.

    Recall what a ref variable or object does. It can track or preserve the value it references, even when the function component in which it is defined re-renders. This means that myNameRef will always remember that it references the input element. It will not lose track of the element that it references.

    The following code listing gets and displays the value of the input control after the button is clicked:

    JavaScript
    import React, { useRef } from "react";
    
    const MyName = () => {
        // initialize a ref object with useRef
        const myNameRef = useRef();
    
        // event handler for button click
        const onBtnClick = () => {
            alert(`Your name is ${myNameRef.current.value}`);
        }
    
        return (
            <div style={{width: '60%', margin: '20px auto', textAlign: 'center'}}>
                <input type='text' placeholder="Enter your name" ref={myNameRef} />
                <button type='submit' onClick={onBtnClick}>Submit</button>
            </div>
        )
    }
    
    export default MyName;

    Remember that the value referenced by a ref object can be accessed through its current property. Hence, the current property of myNameRef references the input element. As seen on line 14, we set myNameRef as value to the ref attribute. In the DOM API, the input element is actually an object of HTMLInputElement.

    Since myNameRef.current references an HTMLInputElement, we obtain the text entered by the user through the value property. If we wanted to save the text entered by the user, we would simply assign the value property of the element to another variable:

    JavaScript
    // save the text entered by the user
    const name = myNameRef.current.value;

    One thing you should realize is that we are able to reference DOM elements for dynamic data retrieval without the need for DOM API functions like document.getElementById(), document.getElementsByClassName(), or document.querySelector(). Also, there is no need to use jQuery library. For DOM API or jQuery, we would need to specify an id attribute on HTML element, and then retrieve the value in a useEffect hook function, such as in the following:

    JavaScript
    useEffect(() => {
        // get reference to input and retrieve value with DOM API
        const inputElem = document.getElementById('myName');
        const name = inputElem.value;
        
        // alternatively with jQuery
        // get reference to input and retrieve value with jQuery
        const inputElem2 = $('#myName');
        const name2 = $(inputElem2).val();
    }, []);

    However, when using useRef to reference elements, we rather need to specify a ref attribute and set its value to a ref object initialized with useRef(). In this case, there will be no need for an id attribute on the element.

    Manipulating DOM Elements

    Sometimes, we may need to dynamically manipulate DOM elements, such as update their content or apply styles to them. In such cases, we may need to get a reference to the DOM elements that need to be updated dynamically.

    Luckily for us, we have already covered the initial part of manipulating DOM elements: getting a reference to DOM elements. The previous section on Referencing DOM Elements, which is a precursor to the discussion in this section, already dealt with this first part.

    In listing 18, we displayed the name entered by the user with the javascript alert() function. Let’s consider an alternative approach in which we dynamically update a DOM element to display the name in the document page.

    JavaScript
    import React, { useRef } from "react";
    
    const MyName = () => {
        // initialize a ref object with useRef
        const nameInputRef = useRef();
        const nameRef = useRef('');
    
        // event handler for button click
        const onBtnClick = () => {
            nameRef.current.innerText = `Your name is ${nameInputRef.current.value}`;
        }
    
        return (
            <div style={{width: '60%', margin: '20px auto', textAlign: 'center'}}>
                <input type='text' placeholder="Enter your name" ref={nameInputRef} />
                <button type='submit' onClick={onBtnClick}>Submit</button>
                <p ref={nameRef}></p>
            </div>
        )
    }
    
    export default MyName;

    On line 17, we introduce a <p></p> tag which will be used to display information about the name entered by the user. The content of this <p></p> tag is blank, hence no text is initially displayed. To be able to reference it after render, we set its ref attribute to nameRef, a ref object initialized on line 6 with useRef().

    The following is a rendered view and usage of MyName component in listing 21:

    We have said that when a ref object is set as value to a ref attribute of a DOM element, its current property references the DOM element. For nameRef, it references the <p></p> tag, and we can, for example, dynamically set its content with innerText property. We do this on line 10 in the onBtnClick event handler, as shown below:

    JavaScript
    nameRef.current.innerText = `Your name is ${nameInputRef.current.value}`;

    If you are used to working with the DOM API, you can use ref objects to access DOM objects for manipulating the document. For example, knowing that nameRef references DOM element p in listing 21, we can change its font style with the following code:

    JavaScript
    nameRef.current.style.fontStyle = 'italic';

    You will rarely see code such as this in React applications. We are only demonstrating that ref object can be used as a reference to a DOM element, which eliminates the need for DOM API calls like document.getElementById(), document.querySelector(), etc.

    For instance, rather than set the content of the DOM element p with innerText property, we can use React useState hook to update its content. The following code listing is an example:

    JavaScript
    import React, { useRef, useState } from "react";
    
    const MyName = () => {
        // initialize a ref object with useRef
        const nameInputRef = useRef();
        const [nameInfo, setNameInfo] = useState('');
    
        // event handler for button click
        const onBtnClick = () => {
            setNameInfo(`Your name is ${nameInputRef.current.value}`);
        }
    
        return (
            <div style={{width: '60%', margin: '20px auto', textAlign: 'center'}}>
                <input type='text' placeholder="Enter your name" ref={nameInputRef} />
                <button type='submit' onClick={onBtnClick}>Submit</button>
                <p>{nameInfo}</p>
            </div>
        )
    }
    
    export default MyName;

    It is not recommended to use the value of a ref object directly in JSX code. For example, replcacing line 17 of listing 24 with the following isn’t recommended:

    JavaScript
    <p>{nameInputRef.current.value}</p>

    We should try as much as possible to avoid using the value of ref objects in application UI code.

    When to use useRef

    In this section, we outline some of the use cases of React useRef hook.

    • Persist Values Without Re-render: The useRef hook should be used when there is the need to preserve some values between re-renders and when at the same time, we do not want to trigger a component re-render after we update the value. If there is the need to trigger a component re-render for the updated value to be visible in the UI, then useRef isn’t the right hook. In this case, useState has to be used.
    • Access DOM Element Values: The useRef hook can be used to dynamically retrieve values from DOM elements. For example, after a user clicks on the submit button of a form, the user’s input can be retrieved through references to each form input. For form inputs, using useRef to retrieve the values can be very efficient. If useState is used, it will trigger a re-render for every character the user types.
    • Manipulate DOM Element: As demonstrated in listing 21, DOM elements can be manipulated with the use of the useRef hook, for example their content or styles. However, the content of DOM elements are commonly updated with state variables initialized with useState hook.

    Key Considerations

    When working with the useRef hook, there are some factors that need to be considered. Complying with ensure best performance and reliability of React applications and also conform to best programming practices.

    Avoid Refs in JSX

    For React best practices, it is highly recommended to avoid the use of ref object values directly in JSX. If there is the need to display variables in the application UI, then state variables have to be used.

    For example, the following code isn’t recommended:

    JavaScript
    import React, { useRef } from 'react';
    
    const MyComponent = () => {
        const refObject = useRef(10);
    
        return (
            <div>You are {refObject.current} years old.</div>
        )
    }
    
    export default MyComponent;

    On line 7 a ref object value is directly used in the JSX code. This practice isn’t recommended. If there is the need to use variables in JSX, then the recommended approach is to use state variables in the UI code.

    Avoid Recreating Initial Values

    When initializing a ref object with useRef(), it is important to avoid passing functions that perform resource intensive tasks as argument to the useRef() call. For example:

    JavaScript
    const refObject = useRef(getInitialValue());

    Keep in mind that the initial value passed to useRef() is used and an assigned to the ref object’s current property only in the first render of the component. For re-render, the initial value is not used. It is rather the value that was persisted before the re-render which is used.

    If we pass a function to useRef(), the function will be called on every re-render. However, its return value will not be used when the component re-renders. This becomes a waste of processing time and resource if the function performs expensive task.

    If we need to call a function to get the initial value at the first render, we can pass null to useRef(), and then use conditional logic to determine if the initial value was initially set or not:

    JavaScript
    const refObject = useRef(null);
    
    // Check to see if current property is null, 
    // then we call the function. If not null, then
    // it means the initial value was previously set
    if (refObject.current === null) {
        refObject.current = getInitialValue();
    }

    Reading and Writing Refs

    We have earlier indicated that ref object values need not be used in the JSX code. It is highly recommended that we read or write to ref objects in event handlers and or effect handlers.

    For example, in listing 13, we updated refCounter in a button click event handler:

    JavaScript
    const onBtnClick = () => {
        refCounter.current += 1;
        nonRefCounter += 1;
    }

    Once the application UI is rendered, it is best to ensure that interactions with ref object values are done in response to events that occur in the application or when processing side effects.

    Summary

    React useRef is a hook that allows a function component to create a reference to a value and track it between component re-renders. It can also be used to reference DOM elements and retrieve their values or manipulate them.

    Updates to ref objects to do trigger a re-render of the component. Hence, they are typically used for storing values that are not expected to be visible in the application UI. They are used internally to keep reference to values.

    Since useRef is a hook, its usage must comply with the Rules of Hooks in React. Notably, useRef has to be used in the top level of the function component.

  • Understanding React useState Hook

    React useState is a hook that allows function components to manage the state of their variables. It enables function components to keep track of the values of their variables between multiple calls. It is one of state management hooks provided by React.

    When the value of a state variable initialised with useState() is updated, React automatically calls the function component to re-render the application view. This implies that the useState hook is crucial for keeping track of values that are rendered in the application view, and which we will want the UI to automatically update when the state values change.

    In this post, we will learn to understand the React useState hook and how to correctly update state data initialised with useState().

    Stateless vs Stateful Functions

    To better understand React useState hook and why we may need it, it will be helpful to understand the difference between stateless and stateful functions.

    Stateless Functions

    When we say a function is stateless, we imply that the function does not remember the state of data it contained between multiple calls. That is, if the function is called and completes its task, the state of any data that it contained are lost. If we make another call to the same function after an earlier call, it will not be able to remember or retrieve the state of the variables defined in the function. In other words, the data contained in the variables defined in the function in the previous call are lost.

    Consider code listing 1 below:

    JavaScript
    function addToList(number) {
        // define variable and assign default value
        let list = [];
        
        // append number to array
        list.push(number);
        
        // log the total value
        console.log(list);
    }
    
    // call function multiple times with values
    addToList(50);
    addToList(70);
    addToList(100);

    In listing 1, the addToList() function creates an array on line 3. On line 6, the function adds the number passed as argument to the list of items. From line 13 to line 15, we call addToList() to add numbers the list.

    If we run the javascript code in listing1, we will observe that the log of the array for the second and third calls to addToList() do not contain the previous values added. The following is a sample run in the browser:

    stateless function

    The first call to addToList() with the value 50 is seen in the output as [50], thus list array contains a single item. This is what we expect. However, when the second call to addToList() is made with the value 70, we see from the second output that the array still contains a single item, [70]. The previous item, 50, has been lost. The same applies to the third call to addToList(), as seen in the third output, [100].

    What happens is that when the function completes its task, it loses track of the state of the data it contained. Hence, as subsequent calls are made, the function is not able to remember the state of its previous data.

    Functions that are not able to track the state of their data between multiple calls, as seen in listing 1, are stateless functions. This is the default behaviour of javascript functions, and is also the default in almost all programming languages.

    Stateful Functions

    Contrary to stateless functions, stateful functions remember the state of their data between multiple calls. If subsequent calls are made, a stateful function can remember the state of its data in the previous call and continue to work with the data in the current call.

    In the past years, React apps were developed using ES6 classes. Classed-based React components made it simple to keep the state of data in objects. In modern React apps development, functions are used to create components. This shift has been necessitated by the introduction of hooks in React 16.8.

    The shift to function components implies that, for components that need to track the state of their data, there should be a way to create stateful function components that can track the state of data between multiple function calls. This is what React useState addresses. React useState makes it possible for function components to recall the state of variables in previous calls into current calls.

    Another React feature that makes a function component stateful is the useRef hook. One difference between useState and useRef hooks is that when a state variable returned from a useState() is updated, it causes a re-render for the UI to update. However, an update to a state variable returned from useRef() hook does not cause a re-render.

    Another difference between useState() and useRef() hooks is that a state variable from useState() is updated by calling a special setter function which sets the new state value whiles a state variable from useRef() hook can be updated by directly setting it to the new value. The discussion on understanding useRef hooks delves deeper into how useRef works.

    Let’s take an extensive look at how to make a function component stateful with useState.

    useState Makes Function Component Stateful

    React useState is a hook that allows a function component to track its data, or variables, between multiple calls (render and re-renders). This means that useState makes a function component stateful. It makes the function component remember state variables between multiple renders. When a state variable intialised with useState is updated, the component re-renders for the UI to update.

    When React needs to render or update a component, it makes a call to the function component. For components that may be re-rendered, it implies that multiple calls to the function component are made. In these updates (re-renders), in which the function component is called multiple times, a stateful function component can remember the content of state variables in the previous call and use them in the current call..

    How to use useState Hook

    As we have indicated, React useState hook makes a function component stateful. It enables a function component to keep track of some or all of its data variables.

    It must be emphasised that React hooks are used in function components only. They are not used in class components. Additionally, the use of hooks in function components is governed by some rules. For instance, Hooks must be used at the top of the body of the function component. We will go by the Rules of Hooks in the rest of this discussion.

    To use React useState hook in a function component, we first need to import useState from React. This has to be done at the top of the function component file:

    JavaScript
    import { useState } from 'react';

    Within the body of the function component, we initialise a state variable that needs to be tracked in a function component. We initialise a state variable with useState() call. useState() accepts a default value that will be set to the state variable that gets created:

    JavaScript
    const state_var = useState('Initial Value');

    What we pass to useState() call depends on the type of the data that we want to create:

    JavaScript
    const numericStateData = useState(40);     // numeric state data
    const booleanStateData = useState(true);   // boolean state data
    const stringStateData = useState('Sample Text');  // string state data
    const arrayStateData = useState([]);       // array state data
    const objectStateData = useState({});      // object state data

    We can even initialise state variables to null or undefined with useState():

    JavaScript
    // state data initially set to null
    const someStateData = useState(null);  
    
    // state data initially set to undefined     
    const anotherStateData = useState(undefined);  

    Remember that what we pass to useState() is just an initial value we want to set to the state variable that gets created. We can update the value in the state variable later with a special function that useState() creates for us. We will see how this is done shortly.

    useState Returns an Array

    There is more that we need to know about useState() initialisations which I did not mention listing 4 and listing 5 above. It is the reason I did not show the earlier useState() initialisations in a function component. Now is the right time to know more about the data returned from useState() initialisation calls.

    A call to useState() returns an array that contains two items: the state variable and a state setter function. The setter function is used to update the data stored in the state variable. We can access the state variable at index 0, whiles the state setter function is at index 1:

    JavaScript
    import React, { useState } from 'react';
    
    const App = () => {
        // initialise state data and set initial value to zero (0)
        const stateData = useState(0);
        
        // variables for state data and setter function
        const count = stateData[0];     // the state variable
        const setCount = stateData[1];  // the state setter function
    }

    Rather than use array indexing to refer to the state variable and setter function, we can use array destructuring to get the state variable and the setter function from the useState() initialisation, all in just a single line:

    JavaScript
    import React, { useState } from 'react';
    
    const App = () => {
        // variables for state data and setter function
        const [count, setCount] = useState(0);
    }

    The array destructuring approach, used in listing 7, enables us to assign names to the array elements returned from useState() in just a single call. Due to its simplicity and elegancy, we will be using this approach in the rest of the discussion.

    You are at your own liberty to choose names that you will assign to the state variable and the setter function returned by useState(), The names should however obey the variable and function naming restrictions in javascript. A very common practice is to precede the state setter function name with set, combined with the name assigned to the state variable. The following code listing has some examples:

    JavaScript
    import React, { useState } from 'react';
    
    const App = () => {
        const [count, setCount] = useState(0);
        const [age, setAge] = useState(0);
        const [names, setNames] = useState([]);
    }

    When updating the value of a state variable initialised with useState(), it is inappropriate to directly set the value using an assignment statement. For example, it is wrong to set count to a new value as demonstrated in the following code listing:

    JavaScript
    const [count, setCount] = useState(0);
    
    // This update to state variable is wrong
    count = 10

    If we need to update a state variable, for example count to a new value, we need to call the state setter function. In this case, we will call setCount() to set the new value of count.

    The following code shows the correct way to update the value of a state variable returned by useState(). The update is done using the state setter function:

    JavaScript
    const [count, setCount] = useState(0);
    
    // set the state variable, count, to a new value
    setCount(10);

    As seen in this code listing, we set a new value to the count variable using the state setter function setCount(). This is the correct way to update a state variable initialised with useState() hook. We will see working examples shortly.

    Working with useState Hook

    To put what we have covered so far into practice, we will consider a very simple function component that updates a heading text. As we enter text in an input control, the heading will be updated using a state setter function returned from useState() initialisation.

    JavaScript
    import React, { useState } from "react";
    
    // default heading text
    const DEFAULT_HEADING = 'Heading';
    
    const Heading = () => {
        // initialise state data for heading
        const [heading, setHeading] = useState(DEFAULT_HEADING);
    
        return (
            <div style={{textAlign: 'center'}}>
                <h1>{heading}</h1>
                <input 
                    type='text' 
                    onChange={(e) => {
                        const value = e.target.value;
                        setHeading(value.length ? value : DEFAULT_HEADING);
                    }} 
                />
            </div>
        )
    }
    
    export default Heading;

    From listing 11, useState() is called to initialise and return a state variable and a setter function on line 8. In the JSX returned from the function component, the h1 element is set to the heading state variable. In the onChange event handler for the input element, we call setHeading() to update the heading state variable to the entered text.

    A sample run in the browser is shown below:

    useState hook

    As the input text changes, setHeading() is called to update heading state variable.

    State Update Triggers Re-Render

    In listing 11, we update the state variable heading by calling setHeading(). This occurs whenever the text entry in the input control changes. Updates to the text entry immediately updates the heading text.

    What actually happens is that when the state variable updates, the UI component, in this case Heading, is automatically called to re-render. In other words, the function component gets called to update its UI. This is the behaviour of updates to a state variable initialised with a useState() call. If the state variable is updated, the function component containing the state variable is automatically called to re-render.

    But is there really a way to prevent a re-render when we update a state variable?, Yes, there is. If we do not want a component to be called automatically to update the UI when a state variable updates, then the state variable should not be initialised with useState(). In this case, we will rather initialise the state variable with useRef().

    An update to a state variable initialised with useRef() will not automatically cause a re-render of the component. We discuss useRef hook in a separate post covering understanding of React useRef hook.

    A Look at State Setter Functions

    A state setter function, which is needed to update state variables, accepts either the new value or a function that needs to be called to return the new value. After updating a state variable with a state setter function, the function component will be called to re-render for the UI to be updated.

    A state setter function has the following signature:

    JavaScript
    setFunction(new_value | () => { return new_value});

    As can be seen, we can pass a new value, or a function that will return a new value, to the state setter function. If a value or an expression that results in a value is specified, the new value will replace the current value of the state variable. For example:

    JavaScript
    const [count, setCount] = useState(0);
    
    // set state variable to already known value
    setCount(20);
    
    // set state variable to result of an expression
    setCount(20 * Math.PI);

    On the other hand, if a function is specified as argument to the state setter function, then the specified function will be called to return the new value. Consider the following code listing:

    JavaScript
    const [count, setCount] = useState(0);
    
    // set state variable to a function that will 
    // detemine and return the new value
    setCount(() => {
        return 20;
    });

    In listing 14, we pass to setCount() an anonymous function that will determine and return the new value of the state variable. The value that is returned from the function will be used to update the state variable.

    Sometimes, we may need the previous value in order to determine the new value that has to be returned. For example, think of a counter variable that calculates the new value by adding 1 to the currently existing value. In this case, we will need to have the current value in order to calculate the new value to be set.

    When we pass a function to the state setter function, the current value of the state variable is passed as argument. If the new value to be returned depends on the current value, then this becomes a great opportunity. We can determine the new value based on the current value of the state variable that is passed:

    JavaScript
    // set state variable to a function that will detemine the new value
    setCount((currValue) => {
        return currValue + 1;
    });

    Observe that the current value is passed to the anonymous function that returns the new value. We return the new value by adding 1 to the current value of the state variable.

    In fact, we can even rewrite listing 15 as the following code listing which will yield the same result:

    JavaScript
    // set state variable to a function that will detemine the new value
    setCount((currValue) => currValue + 1);

    The following is a working example of a component that updates state data:

    JavaScript
    import React, { useState } from 'react';
    
    const ClicksCounter = () => {
        // initialise a state variable
        const [count, setCount] = useState(0);
        
        return (
            <div style={{textAlign: 'center'}}>
                <h2>Button Clicks Counter</h2>
                <hr />
                <p>You clicked {count} time{count == 1 ? '' : 's'}.</p>
                <button onClick={() => setCount((currCount) => currCount + 1)}>
                    Click to Count
                </button>
            </div>
        )
    }
    
    export default ClicksCounter;

    The onClick handler of the button calls the state setter function setCount() to update the number of times the button has been clicked. We pass an anonymous function that receives the current value of count as currCount and add 1 to it.

    A sample run of listing 17 in the browser is shown below:

    usestate

    Updating State Data

    We have already emphasised that the initial value for a state variable is specified as argument to useState() initialisation call. Any other call to set a new value to a state variable is actually an update. We have also said that all updates to a state variable must be done using the state setter function returned from useState().

    For example, In listing 10, we demonstrated how to update state variable. However, there is more that we need to know. We need to have an in-depth look at setter functions that upate state variables. Understanding how to correctly update state variables is key to developing an interactive and more dynamic React application.

    Updating State with Setter Function

    To update state data, we need to call the state setter function returned by useState() and pass the new value as argument. If we set the state variable to a new value directly, our function component will not be called to re-render and update the UI. To demonstrate this, let’s consider the following code listing:

    JavaScript
    import React, { useState } from 'react';
    
    const ClicksCounter = () => {
        // initialise a state variable
        let [count, setCount] = useState(0);
        
        return (
            <div style={{textAlign: 'center'}}>
                <h2>Button Clicks Counter</h2>
                <hr />
                <p>You clicked {count} time{count == 1 ? '' : 's'}.</p>
                <button onClick={() => setCount((curVal) => curVal + 1)}>
                    Click to Count
                </button>
            </div>
        )
    }
    
    export default ClicksCounter;

    Listing 18 above is a minor modification to listing 17 The only difference is that we have changed the state data declaration on line 5 from const to let. We have done this because we want to be able to set a new value to count. const declaration will not allow us to do this. Although not recommended, we are using let because we only want to demonstrate that setting state variable directly will not lead to component update.

    If we run the code in listing 18, the result should be the same as in listing 17.

    Now, on line 13, let’s update the code to set the count state variable directly:

    JavaScript
    <button onClick={() => count = count + 1}>Click to Count</button>

    Observe that we are setting the count state variable directly rather than calling setCount(). If we run the application, we will notice that clicking on the button does not update h2 element for count:

    usestate - set state directly - no component update

    Because we directly set a new value to count without using the state setter function, our function component is not automatically called to re-render and update the UI.

    If we change line 13 back to set the new value with setCount(), then the application should be working as expected. The understanding here is clear: we should only update state variables using the state setter function returned from useState().

    Updating Array Data

    When updating state data that is an array, we need to treat the data as immutable. By this, we imply that once we have initialised the array data, we should treat it as one that we cannot modify directly, although we can. If we need to update the array data, we will have to initialise the array variable to a new array data.

    To better understand it, let’s consider the following code in which we create an array data:

    JavaScript
    // initialise array data
    let names = ['Daniel', 'Joe'];

    In classic javascript, we can update the names array, for instance add or remove items, by calling push() or pop() methods respectivley. For example:

    JavaScript
    // initialise array data
    let names = ['Daniel', 'Joe'];
    
    // add new item
    names.push('Emmanuel');

    However, when developing React applications, we need to treat array state variables, such as names, as immutable. We do not need to directly modify the array data as was done in listing 21.

    With data immutability, the recommended approach is to initialise the array state variable, in this case names, to a new array data. There are several ways to do this but the newest and friendier way is the use of the javascript spread syntax:

    JavaScript
    // initialise array data
    let names = ['Daniel', 'Joe'];
    
    // add new item and assign to same variable
    names = [...names, 'Emmanuel'];
    
    // add new item and assign to new variable
    const updatedNames = [...names, 'Joyce'];

    On lines 2, 5, and 8, the expressions on the right side of the assignment operator create a new array, and a reference to these new array are assigned to the variables on the left of the assignment operator. The variables on the left side of the assignment operator are actually references to the array data that are created. Anytime we need to modify an array data, we need to create a new one rather than modify the existing array data.

    The following code listing is an example React component that demonstrates the update of array state data. We create a new array rather than modify the current state of the array data.

    JavaScript
    import React, { useState } from "react";
    
    const Names = () => {
        // state variables for name entry and names list
        const [names, setNames] = useState([]);
        const [name, setName] = useState('');
    
        // called when form needs to be submitted
        const onFormSubmit = (e) => {
            // prevent form submit
            e.preventDefault();
    
            // add to names list if name entered
            name.length > 0 ? setNames((currNames) => [...currNames, name]) : '';
    
            // reset input text to empty string
            setName('');
        }
    
        return (
            <div>
                <form onSubmit={onFormSubmit}>
                    <input 
                        type='text' 
                        value={name} 
                        placeholder='Enter Name'
                        onChange={(e) => setName(e.target.value)} 
                     />
                    <button type='submit'>Add Name</button>
                </form>
                
                <hr />
                <ul>
                    { names.map((name, idx) => <li key={idx}>{name}</li>) }
                </ul>
            </div>
        )
    }
    
    export default Names;

    On line 5, we initialise the state variable names to an empty array in the useState() call. In addition to the names state variable, useState() returns a state setter function which we use to update the names state data, in this case an array.

    In the JSX returnd from the component, an onChange handler for the input sets the text entered to the name state variable. When the form is submitted, the onFormSubmit handler updates the names state data by creating a new array and setting it to the state setter function setNames(), as seen on line 14 in listing 23:

    JavaScript
    // add to names list if name entered
    name.length > 0 ? setNames((currNames) => [...currNames, name]) : '';

    From the above code listing, we pass an anonymous function to setNames(). This anonymous function receives the current names list as currNames parameter, and then creates and returns a new array by adding name to the currNames state array. In this example, we use the javascript spread syntax to add the new name:

    JavaScript
    setNames((currNames) => [...currNames, name])

    You may be tempted to use the names state variable with the javascript spread syntax to create the new array such as in the following:

    JavaScript
    // add to names list if name entered
    setNames([...names, name])

    I must say that this will likely work for you in most cases. However, it must be noted that React queues calls to state setter functions. If several calls to a state setter function are so fast that a component is not able to update quickly, then you may be passing a state value which isn’t updated yet to the state setter function. The result will be that the UI may not correctly update to reflect expected data.

    Remember what we talked about earlier: if the new state value depends on the current value of the state variable, then it is a good practice to pass a function to the state setter function and receive the current state value.

    In the example discussed earlier, we added a new name to the names state variable. This implies that the new array that we create depends on the current names array data. Therefore, we pass a function to the state setter function and receive the current state of the names array, which we use to create and return a new array data:

    JavaScript
    setNames((currNames) => [...currNames, name])

    A sample run of listing 23 is shown below:

    Updating Object Data

    Similarly to array state data, object state data must also be treated as immutable. If we need to update a state data that is an object, we do not need to modify it directly. We rather need to create a new one. We do this by making a copy of the current object data and perform the update in the new object that is being created:

    Suppose we have the following object data:

    JavaScript
    // create an object data
    let Person = {
        name: 'Daniel',
        role: 'Full Stack Developer'
    };

    Let’s also suppose that we want to perform an update to the name property. In classic javascript, we can modify the Person object directly by setting the name property to a new name:

    JavaScript
    // create an object data
    let Person = {
        name: 'Daniel',
        role: 'Full Stack Developer'
    };
    
    // update the name
    Person.name = 'Emmanuel';

    In React, we have to treat state data that are objects as immutable. Rather than modify the Person object by setting the new name directly, we have to initialise a new object data as a copy of the one we want to modify, and then perform the update in the new object initialisation:

    JavaScript
    // create an object data
    let Person = {
        name: 'Daniel',
        role: 'Full Stack Developer'
    };
    
    // update and set to same object reference
    Person = {...Person, name: 'Emmanuel'};
    
    // update and set to a new object reference
    let updatedPerson = {...Person, name: 'Emmanuel'}

    Let’s consider an example in which we will update an object data. Let’s suppose we have the following component that displays the profile of a developer:

    JavaScript
    import React from "react";
    
    const Profile = (props) => {
        return (
            <table>
                <tbody>
                    <tr>
                        <td>Name:</td>
                        <td><strong>{props.profile.name}</strong></td>
                    </tr>
                    <tr>
                        <td>Email:</td>
                        <td><strong>{props.profile.email}</strong></td>
                    </tr>
                    <tr>
                        <td>Role:</td>
                        <td><strong>{props.profile.role}</strong></td>
                    </tr>
                </tbody>
            </table>
        )
    }
    
    export default Profile;

    To be able to update the developer profile, we will also consider the following component which will display a form to collect new information about the developer:

    JavaScript
    import React, { useState } from "react";
    
    const ProfileEditor = (props) => {
        const [name, setName] = useState('');
        const [email, setEmail] = useState('');
        const [role, setRole] = useState('');
    
        const onSubmit = () => {
            props.onUpdate({name, email, role});
        }
    
        const disableUpdateBtn = () => {
            return role === '' || name == '' || email == '';
        }
        
        return (
            <div style={{margin: '20px 0'}}>
                <form onSubmit={onSubmit}>
                    <div style={{marginBottom: '10px'}}>
                        <input 
                            type='text' 
                            value={name} 
                            placeholder="Enter name" 
                            onChange={(e) => setName(e.target.value)}
                            style={{minWidth: '90%', marginBottom: '10px'}}
                        />
                        <input 
                            type='email'
                            value={email}
                            placeholder="Enter email"
                            onChange={(e) => setEmail(e.target.value)}
                            style={{minWidth: '90%', marginBottom: '10px'}}
                        />
                        <select 
                            value={role} 
                            onChange={(e) => {
                                // get selected index
                                const selectedIdx = e.target.selectedIndex;
                                
                                setRole(e.target.options[selectedIdx].value)}
                            }
                            style={{minWidth: '90%'}}
                        >
                            <option value=''> --Select role --</option>
                            <option value='Back-End Developer'>
                                Back-End Developer
                            </option>
                            <option value='Front-End Developer'>
                                Front-End Developer
                            </option>
                            <option value='Full Stack Developer'>
                                Full Stack Developer
                            </option>
                        </select>
                    </div>
                    <button type='submit' disabled={disableUpdateBtn()}>
                        Update
                    </button>
                    <button type='button' onClick={() => props.onClose()}>
                        Close
                    </button>
                </form>
            </div>
        )
    }
    
    export default ProfileEditor;

    The code listing for our final component which uses the Profile and ProfileEditor components, and within which we will update the state variable for the profile, is shown below:

    JavaScript
    import React, { useState } from "react";
    import Profile from "./Profile";
    import ProfileEditor from "./ProfileEditor";
    
    const Developer = () => {
        const [profile, setProfile] = useState({name: '', email: '', role: ''});
        const [edit, setEdit] = useState(false);
    
        const onUpdate = (data) => {
            // update profile by initialising a new object
            setProfile({
                name: data.name,
                email: data.email,
                role: data.role
            });
        }
    
        const hideEditor = () => {
            setEdit(false);
        }
    
        return (
            <div style={{width: '40%', margin: '0 auto'}}>
                <div>
                    <h1 style={{display: 'inline'}}>Developer Profile</h1>
                    {!edit && (
                        <span>
                          <a href='#' onClick={() => setEdit(true)}>Edit</a>
                        </span>
                    )}
                </div>
                <hr />
    
                {edit && (<ProfileEditor 
                            onUpdate={onUpdate} 
                            onClose={hideEditor} 
                          />
                        )}
    
                <Profile profile={profile} />
            </div>
        )
    }
    
    export default Developer;

    The key area to consider in listing 33 is the implementation of the onUpdate() function:

    JavaScript
    const onUpdate = (data) => {
        // update profile by initialising a new object
        setProfile({
            name: data.name,
            email: data.email,
            role: data.role
        });
    }

    As can be seen, we pass a new object that contains the new profile information to setProfile() setter function. If we initialise state data with useState(), then we should treat the data as immutable. We do not need to modify the data directly but rather initialise a new data as a modified copy of the initial data and pass to the state setter function for update.

    A sample run of listing 33 in the browser is shown below:

    When the application first launches, the profile Name, Email, and Role are all set to empty text as seen on line 6 in listing 33.

    JavaScript
    const [profile, setProfile] = useState({name: '', email: '', role: ''});

    To update the profile object, we first display the profile editor form to enter name, email, and select developer role. When the form is submitted, the form data is received in the onUpdate() function defined in the Developer component. The important thing to notice is that we pass a new object data which contains the new state information for the profile object. In passing the new profile information to setProfile(), we did not modify the profile object directly.

    What if We Mutate Array and Object State Data?

    We have said that state data which are arrays or objects must be treated as immutable. We should not modify them for update but rather make a copy of them and then perform the update during initialisation of the new data. So what happens if we mutate the array or object data directly? The result will be unexpected application behaviour and or bugs.

    To demonstrate unexpected behaviour of mutating state data that are arrays or objects, we will use a code listing we have already seen. To avoid scrolling back and forth, we will reproduce listing 23 below:

    JavaScript
    import React, { useState } from "react";
    
    const Names = () => {
        // state variables for name entry and names list
        const [names, setNames] = useState([]);
        const [name, setName] = useState('');
    
        // called when form needs to be submitted
        const onFormSubmit = (e) => {
            // prevent form submit
            e.preventDefault();
    
            // add to names list if name entered
            name.length > 0 ? setNames((currNames) => [...currNames, name]) : '';
    
            // reset input text to empty string
            setName('');
        }
    
        return (
            <div>
                <form onSubmit={onFormSubmit}>
                    <input 
                        type='text' 
                        value={name} 
                        placeholder='Enter Name'
                        onChange={(e) => setName(e.target.value)} 
                     />
                    <button type='submit'>Add Name</button>
                </form>
                
                <hr />
                <ul>
                    { names.map((name, idx) => <li key={idx}>{name}</li>) }
                </ul>
            </div>
        )
    }
    
    export default Names;

    Now let’s consider the implementation of the onFormSubmit event handler:

    JavaScript
    // called when form needs to be submitted
    const onFormSubmit = (e) => {
        // prevent form submit
        e.preventDefault();
    
        // add to names list if name entered
        name.length ? setNames((currNames) => [...currNames, name]) : '';
    
        // reset input text to empty string
        setName('');
    }

    Let’s also comment the call to setName() on line 10 and update line 7 to use the names array as argument to setNames() setter function. We will also use the push() method of the names array to add the new name.

    The code in the onFormSubmit handler should look similar to the following:

    JavaScript
    // called when form needs to be submitted
    const onFormSubmit = (e) => {
        // prevent form submit
        e.preventDefault();
        
        // add new name 
        names.push(name);
    
        // add to names list if name entered
        name.length ? setNames(names) : '';
    
        // reset input text to empty string
        // setName('');
    }

    On line 7, we mutate the state data referenced by names array by calling push() to add the new name:

    JavaScript
    names.push(name)

    Then on line 10, we pass the same names array to setNames() rather than create a new array data:

    JavaScript
    // add to names list if name entered
    setNames(names)

    Because we did not create a new array but rather mutated the names array directly before setting as argument to setNames(), the result will be unexpected. If we run the application, we will notice that the UI does not update to show the names list when we click on the Add Name button:

    In listing 23, the names list was populated after clicking on the Add Name button. However, in this example, the list of names is not populated after clicking the Add Name button. The reason is that when we do not create a new object or array but rather modify and set the same data reference, the function component will not be called to re-render and update the UI.

    So, if the UI does not update, does the internal code update the state data? To know the answer, continue to type in the input control. You should now see the list populated with with names.

    But why is the list not populated when we click on the Add Name button but rather when we type in the input control? Well, the component is called to update the UI at this time because the onChange handler of the input control calls the state setter function setName() which triggers a re-render., otherwise we would not see the list of names.

    One thing is clear: if we mutate an array or object data rather than initialise a modified copy, then the component will not be called to update the UI. Therefore, we should always treat state data that we want to be automcatically updated in UI as immutable and initialise modified copies to be set as new state data.

    There may be situations when we may want to track and recall the values of a state data but wouldn’t necessarily want the component to be re-rendered after we update the state data. In such cases, we will need to initialise the state data with useRef().

    Like useState(), the useRef() hook also creates a state variable which can maintain state data between component re-renders. The major difference is that updates to state data initialised with useRef() do not trigger a re-render of the component. The discussion on understanding useRef hook delves deeper into this.

    Key Considerations for Updating State

    • We should only update state variables with state setter functions
    • We should always consider state variables immutable. If we need to update state variables, we need to return a new variable

    Summary

    React useState is a hook that is used to make function components stateful so that they can maintain the state of their data when called multiple times to update the UI.

    To initialise a state data, we call the useState() hook and pass the initial value that should be set the the state data which will be created.

    The useState() initialisation call returns an array which contains two items. The first item at index 0 is the state data which needs to be tracked. The second item at index 1 is a state setter function which is called to update the state data. We can use javascripts array destructuring to obtain the state data and the setter function.

    JavaScript
    // initialise state data
    const [count, setCount] = useState(0);

    When performing an update to a state data, we need to call the state setter function and pass to it the new value.

    JavaScript
    // initialise state data
    const [count, setCount] = useState(0);
    
    // set count to new value
    setCount(40);

    If the new value has to be determined from the current state data, then we need to pass a function to the state setter function. In this case, the function passed to the state setter function will receive the current state data so that we can determine or calculate the new value from the current state data:

    JavaScript
    // initialise state data for count
    const [count, setCount] = useState(0);
    
    // set new count value based on the current value
    setCount((currCount) => currCount + 1);

    To trigger a component re-render when a state data changes, we need to treat a state data as immutable and pass a modified copy to the state setter function for update. If we modify the same state data and pass for update, we will have an unexpected result.

  • How to use DataTables in React Application

    DataTables is a powerful javascript library that is used to generate and manipulate HTML table data in web applications. It has amazing features like paging, sorting, ordering, searching, and filtering abilities. The library has been extensively used in web applications.

    DataTables has commonly been used with jQuery in the past few years. However, it can also be used in front-end javascript libraries like React. Although jQuery can be used in React applications, its use in React applications is highly discouraged, unless it becomes inevitable.

    In this post, we will examine how we can use DataTables library to generate table data in a React application. We will soon observe that DataTables library can be used in React applications without directly using jQuery. Afterall, the React community frowns on the use of jQuery in React applications. We will also see how we can dynamically manipulate table data with a powerful API that the DataTables library exposes.

    Setting Up

    In our path to demonstrate the use of DataTables library in a React application, we will first create a starter react application. We will build on this application by installing DataTables package with npm. Although DataTables has its own CSS styling, it has support for other CSS styling frameworks like Bootstrap and Foundation. In this starter application, we will use the Bootstrap styling for the table that we generate.

    To facilitate a quick setup and get going, we will use create-react-app to quickly generate a starter react application. I assume you already have create-react-app package installed. If you prefer to roll your own without using create-react-app, you can read through the extensive discussion on how to create a react application from scratch without using create-react-app tool.

    Install DataTables Package

    Although you can use any text editor to get along with the discussions in this post, I will be using Visual Studio Code for source code views in discussions that follow.

    If you are using VS Code, click on the Terminal menu, then on New Terminal dropdown menu item. If you aren’t using VS code, you can open a terminal window from your working environment.

    Before we issue the initial command, recall that we will be using Bootstrap 5 CSS styling for the table we intend to generate. You should run the following command to install the DataTables package with Bootstrap 5 CSS styling:

    Bash
    npm install datatables.net-bs5 bootstrap@5.3.3

    If you do not want to use Boostrap 5 styling but rather use the default CSS styling for the DataTables library, then you should run the following command:

    Bash
    npm install datatables.net

    As indicated earlier, we will not need jQuery with DataTables package, hence we did not explicitly install jQuery. However, you may see jQuery in your node_modules directory after running the above command. Even if it exists, we will not use it in our implementation code.

    Create DataTable Component

    In react applications development, it is a good practice to create a components directory to house our own components. However, because this is very small application, we will break that customary rule and create a DataTable react component in the same directory that contains our App.js component.

    In the src directory, or your directory that contains your App.js component, create another file for the DataTable component. You can give it the name DataTable.js, or any name of your choosing such as ReactDataTable.js. In my case, I will name the file DataTable.js. After creating the file, the application directory structure should look similar to the following:

    Click on DataTable.js file, and in the file editor section, enter or paste the following code:

    JavaScript
    import React, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // This will reference the table element
        const tableRef = useRef(null);
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    From line 1, we have imported useRef and useEffect hooks from react.

    useRef to Reference <table></table> Tag

    Since the HTML table will be initialised after React has mounted our DataTable component, we need a reference to the <table></table> tag on line 12. Later, we will use it to initialise with DataTables library. As can be seen, we have a ref prop to the <table></table> tag and have set ref to the tableRef variable that we have initialised with useRef on line 7. When the component has mounted, we will use tableRef, which references the <table></table> tag, to initialise the table.

    useEffect for Table Initialisation

    In addition to the useRef hook, we have also imported the React useEffect hook on line 1. useEffect hook is used to call a function to be executed after a React component has been mounted.

    DataTables library requires the table element to be in the browser DOM for initialisation. Hence, after React has mounted our DataTable component, the <table></table> tag will be available in the browser DOM, and we will need to call a function to initialise the table with DataTables library. React useEffect hook does exactly that. When used in a component, useEffect will run the function that we pass to it as argument after the component has been rendered.

    Table Initialisation

    To generate our table with DataTables library, we need to call a function to perform the initialisation. The following code extends the previous code by using useEffect hook to call a function to initialise the table:

    JavaScript
    import React, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // a reference to the table element
        const tableRef = useRef(null);
    
        // After component is mounted, initialise the table
        useEffect(initTable);
    
        function initTable() {
            // initialise table with DataTables library and return API
            const tableApi = new DataTables(tableRef, {...props});
        }
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    Listing 2 defines initTable() function which spans from line 12 to line 15. initTable() initialises the table with DataTables library by passing tableRef, which references the <table></table> tag as first argument, and the table options passed as props to the component as second argument. On line 10, we call useEffect hook by passing initTable as the function to be run when the component is mounted.

    Since we will not make a call to initTable() in any other part of the component, we can pass an anonymous function to useEffect hook to perform the table initialisation. The following code is a refinement to listing 2:

    JavaScript
    import Reat, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // a reference to the table element
        const tableRef = useRef(null);
    
        // After component is mounted, initialise the table
        useEffect(() => {
            // initialise table with DataTables library and return API
            const tableApi = new DataTables(tableRef, {...props});
        });
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    From listing 3, the function that is called to initialise the table is passed as an anonymous function to useEffect hook. This is a common practice with useEffect hook usage.

    If you are in haste to see a working table in your project, I will advise that you read along for a while. This is because listing 3 has some limitations which we need to address, although it will be able to generate a table for you.

    Preventing Re-Initialisation

    Recall that after a react component is rendered, a useEffect call in the component will execute a function passed as argument. By default, this will happen anytime the component is re-rendered. This means that the table will always be initialised again and again for every re-render of the DataTable component. This wouldn’t be ideal, especially when table data is loaded from an external server. If the table loads data from an external server, the table will always attempt to connect to the server and load the data again on every re-render of DataTable component.

    It will be best if we initialise the table only when the DataTable component is first mounted. Then, on subsequent re-rendering of the DataTable component, we will want to prevent the table data from being reloaded. To do this, we will need to pass an empty array as the second argument to useEffect hook call. This is shown in the following code:

    JavaScript
    import React, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // a reference to the table element
        const tableRef = useRef(null);
    
        // After component is mounted, initialise the table
        useEffect(() => {
            // initialise table with DataTables library and return API
            const tableApi = new DataTables(tableRef, {...props});
        }, []);
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    On line 13, in listing 4, we provide an empty array as the second argument to the useEffect hook call. This will prevent the table from being initialised again anytime the DataTable component is re-rendered. To understand more about the the useEffect hook and its conditional execution, you can take a look at the discussion on understanding useEffect hook.

    Performing Cleanup

    If you remember how we initialised our table with useEffect hook, you will agree that our table was generated outside the realm of React library. We waited for React to mount the DataTable component, after which we explicitly generated the table without React. In this sense, we should undo any initialisations we performed when React is about to unmount our component.

    Just as useEffect hook runs a function after a component is mounted, it can also run another function when the component is about to be unmounted from the DOM. This is the time we perform cleanups.

    To inform React about the function that needs to be called for side effects cleanup, we specify the function as the return value in the useEffect hook call. The following code extends listing 4 to include a side effects cleanup function:

    JavaScript
    import React, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // a reference to the table element
        const tableRef = useRef(null);
    
        // After component is mounted, initialise the table
        useEffect(() => {
            // initialise table with DataTables library and return API
            const tableApi = new DataTables(tableRef, {...props});
            
            // return the function that needs to be called to 
            // perform clean-up before the component unmounts
            return () => tableApi.destroy();
        }, []);
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    Observe from line 16 that the return value in the useEffect hook call is an anonymous function. This function will be called before the DataTable component unmounts from the browser DOM. When called, the function will free up resources with a call to destroy() method of the DataTables library API object.

    We now have a component that we can use to generate a table in react application. There is one last addition we will do to the DataTable component to make it complete. For now, we will leave DataTable component as it is and come back to make a final addition.

    Using the DataTable Component

    We are now ready to see a working example of our DataTable component. To generate the table, we will need to define the table columns as well as the data to be populated. We will populate the table with static data, although we can configure it to load data from an external server. If you have worked with DataTables library before, then you know we will need table options data.

    From the file explorer pane, click on App.js file. If you used create-react-app to generate the starter application files, then the content of App.js file should read similar to the following:

    app.js-default-content

    We do not need most parts of the content of App.js file generated by create-react-app. Delete the code inside the return parenthesis, that is, from line 6 to line 21. Also you should delete the existing imports from line 1 to line 2. The content of App.js file should read similar to the following code listing:

    JavaScript
    function App() {
        return (
        
        )
    }
    
    export default App;

    We should now import our DataTable component in App.js file. If you installed DataTables package with Bootstrap 5 styling, then you will need to import Bootstrap CSS files as well.. The following code listing is our updated App.js file:

    JavaScript
    import React from 'react';
    import DataTable from './DataTable';
    
    // import bootstrap CSS file if DataTables for 
    // Bootstrap package (datatables.net-bs5) was installed.
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    function App() {
        return (
            <div className='container'>
                <div className='row mt-5 mb-3'>
                    <div className='col-12'>
                        <h2>Using DataTables in React Application</h2>
                    </div>
                </div>
                <hr />
            
            </div>
        ) 
    }
    
    export default App;

    Although Listing 7 imports our DataTable component, it does not use it yet. We need to define table options which will be passed as props to our table component. In this simple example, we will define data for the columns and data options of the DataTable library. It will be helpful if we create a new file from which we will import the table options data.

    Create a new javascript file in the src directory and name it tableOptions.js. Click on the tableOptions.js file you created, and from the editor view, type or paste the following:

    JavaScript
    // define columns for the table
    export const tableColumns = [
        {data: 'fname', title: 'First Name', className: 'first-name'},
        {data: 'lname', title: 'Last Name', className: 'last-name'},
        {data: 'email', title: 'Email Address', className: 'email'},
        {data: 'gender', title: 'Gender', className: 'gender'}
    ];
    
    // define static data for the rows
    export const tableData = [
        {
            'fname': 'Daniel',
            'lname': 'Oppong',
            'email': 'dan@mail.com',
            'gender': 'Male'
        },
        {
            'fname': 'Sheila',
            'lname': 'Appiah',
            'email': 'sheila@mail.com',
            'gender': 'Female'
        },
        {
            'fname': 'Emelia',
            'lname': 'Cage',
            'email': 'cage@email.com',
            'gender': 'Female'
        }
    ];

    We are only keeping our table data separate from the App component code. To make tableColumns and tableData variables available to our App component code, we precede their declarations with the export keyword.

    With our table options data ready, we should import them into our App component code and generate the table.

    JavaScript
    import React from 'react';
    import DataTable from './DataTable';
    
    // import bootstrap CSS file
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    // import the table options data
    import { tableColumns, tableData } from './tableOptions';
    
    function App() {
        return (
            <div className='container'>
                <div className='row mt-5 mb-3'>
                    <div className='col-12'>
                        <h2>Using DataTables in React Application</h2>
                    </div>
                </div>
                <hr />
    
                <DataTable 
                    ordering={true}
                    columns={tableColumns}
                    data={tableData}
                />
            </div>
        ) 
    }
    
    export default App;

    Line 8 imports tableColumns and tableData from tableOptions.js file. If you are already familiar with DataTables library, then you know there are various ways to specify the source of the table data. In this case, we use static table data.

    We see the use of our DataTable component from line 20 to line 24 and pass tableColumns and tableData as values to columns and data props respectively. We have also included ordering option of DataTables library as prop and set its value to true..

    To test it, run the application with npm start command. This should produce the following in your browser window:

    react datatable

    Working with DataTables API

    Sometimes, it becomes necessary to perform manipulations to the generated table data. For example, we may wish to add, delete, or update some row data. Such operations require that we get a reference to the DataTables API object and use it to perform these operations.

    In DataTable react component, we called an anonymouns function in the useEffect hook with following code:

    JavaScript
    useEffect(() => {
        // initialise table with DataTables library and return API
        const tableApi = new DataTables(tableRef, {...props});
    });

    The variable tableApi is a reference to the DataTables API. To later work with the generated table data, we need access to this DataTables API object. When using our DataTable react component, we can pass a function that needs to be called with this API object as a prop.

    Suppose our DataTable react component has a prop named onInit set to a callback function that should be passed the DataTables API object. We can call the callback function with the tableApi variable as demonstrated in the following code:

    JavaScript
    import React, { useEffect, useRef } from "react";
    import DataTables from "datatables.net-bs5";
    
    // A component to generate table
    const DataTable = (props) => {
        // a reference to the table element
        const tableRef = useRef(null);
    
        // After component is mounted, initialise the table
        useEffect(() => {
            // initialise table with DataTables library and return API
            const tableApi = new DataTables(tableRef, {...props});
            
            // check if function is specicified to receive table API
            if (props.onInit && typeof props.onInit == 'function') {
                props.onInit(tableApi);
            }
            
            // return the function that needs to be called to 
            // perform clean-up before the component unmounts
            return () => tableApi.destroy();
        }, []);
    
        // return UI for this component
        return (
            <div className="table-responsive" style={{padding: '12px'}}>
                <table 
                    className="table table-bordered table-hover no-wrap" 
                    ref={tableRef}
                    style={{width: '100%'}}
                >
                </table>
            </div>
        )
    }
    
    export default DataTable;

    On line 16, we pass tableApi to the callback function after checking if it was specified as a prop.

    In App.js file, let’s define a variable for the DataTables API object as well as a prop function to be passed the table API.

    JavaScript
    import React from 'react';
    import DataTable from './DataTable';
    
    // import bootstrap CSS file
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    // import the table options data
    import { tableColumns, tableData } from './tableOptions';
    
    // variable to reference the table API object
    let tableApi = null;
    
    function App() {
        return (
            <div className='container'>
                <div className='row mt-5 mb-3'>
                    <div className='col-12'>
                        <h2>Using DataTables in React Application</h2>
                    </div>
                </div>
                <hr />
    
                <DataTable 
                    ordering={true}
                    columns={tableColumns}
                    data={tableData}
                    onInit={(api) => {
                        // save reference to the datatable API
                        tableApi = api;
                    }}
                />
            </div>
        ) 
    }
    
    export default App;

    On line 11, we declare a tableApi variable which will later reference the DataTables library API after initialisation. On line 27, we set an onInit prop to an anonymous function which will be called when the table is initialised. This anonymous function will be passed the DataTables API object.

    Now that we have access to the DataTables API object, let’s see an example of how we can perform update to data in the generated table.

    Updating Row Data

    There are so many things that can be done to generated table data. But let’s consider a very simple example. For each row, we will want to toggle first and last names between upper and normal casing. We will provide a button which, when clicked, toggles the text casing of the names.

    First, we need to import useState and useEffect hooks from react in our App component:

    JavaScript
    import React, {useState, useEffect} from 'react';

    Just above the return statement of App component, include the following code:

    JavaScript
    function App() {
        const [capitalize, setCapitalize] = useState(false);
    
        // Toggle names casing whenever capitalize state variable changes
        useEffect(() => {
            if (tableApi) {
                // iterate over each row in the table
                tableApi.rows().every(function() {
                    const data = this.data();
                    const tdFirstName = this.node().querySelector('td.first-name');
                    const tdLastName = this.node().querySelector('td.last-name');
    
                    tdFirstName.innerText = capitalize ? data.fname.toUpperCase() : data.fname;
                    tdLastName.innerText = capitalize ? data.lname.toUpperCase() : data.lname;
                });
            }
        }, [capitalize]);
    
        // return statement here
    }

    From the above code listing, we define a boolean state variable that will indicate whether we need to capitalize the names or not. Then on line 5, the useEffect hook will be executed whenever the capitalize state variable changes. The useEffect hook will be executed when capitalize changes because we have set it as a dependency on line 17.

    To be able to toggle the names casing, let’s include a button as part of the returned JSX. Just after the <hr /> tag and above the DataTable custom element, type or paste the following code for the button:

    JavaScript
    function App() {
        // ... code here intentionally left out
    
        return (
            <div className='container'>
                <div className='row mt-5 mb-3'>
                    <div className='col-12'>
                        <h2>Using DataTables in React Application</h2>
                    </div>
                </div>
                <hr />
    
                <div className='row'>
                    <div className='col-12'>
                        <button 
                            type='button' 
                            className='btn btn-primary'
                            onClick={() => {
                                // change casing
                                setCapitalize((prevState) => !prevState);
                            }}
                        >
                            {capitalize ? 'Normalize' : 'Capitalize'}
                        </button>
                    </div>
                </div>
    
                <DataTable 
                    ordering={true}
                    columns={columns}
                    data={data}
                    onInit={(api) => {
                        // save reference to the datatable API
                        tableApi = api;
                    }}
                />
            </div>
        ) 
    }

    Line 13 to line 26 introduces the button that will be clicked to toggle the capitalize state variable.

    The following code listing is the complete content of App.js file.

    JavaScript
    import React, {useState, useEffect} from 'react';
    import DataTable from './DataTable';
    
    // import bootstrap CSS file
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    // import the table options data
    import { tableColumns, tableData } from './tableOptions';
    
    // variable to reference the table API object
    let tableApi = null;
    
    function App() {
        const [capitalize, setCapitalize] = useState(false);
        
        // Toggle names casing when state changes
        useEffect(() => {
            if (tableApi) {
                // iterate over each row in the table
                tableApi.rows().every(function() {
                    const data = this.data();
                    const tdFirstName = this.node().querySelector('td.first-name');
                    const tdLastName = this.node().querySelector('td.last-name');
    
                    tdFirstName.innerText = capitalize ? data.fname.toUpperCase() : data.fname;
                    tdLastName.innerText = capitalize ? data.lname.toUpperCase() : data.lname;
                });
            }
        }, [capitalize]);
        
        return (
            <div className='container'>
                <div className='row mt-5 mb-3'>
                    <div className='col-12'>
                        <h2>Using DataTables in React Application</h2>
                    </div>
                </div>
                <hr />
                
                {/* JSX for the button */}
                <div className='row'>
                    <div className='col-12'>
                        <button 
                            type='button' 
                            className='btn btn-primary'
                            onClick={() => {
                                // change casing
                                setCapitalize((prevState) => !prevState);
                            }}
                        >
                            {capitalize ? 'Normalize' : 'Capitalize'}
                        </button>
                    </div>
                </div>
    
                <DataTable 
                    ordering={true}
                    columns={tableColumns}
                    data={tableData}
                    onInit={(api) => {
                        // save reference to the datatable API
                        tableApi = api;
                    }}
                />
            </div>
        ) 
    }
    
    export default App;

    To see it working, execute the npm start command for a live view in your browser window. In repeated attempts, click on the button and notice how the first and last names toggle from upper case to normal case.

    react-datatable-update

    As seen from the demonstration so far, we have been able to use DataTables library in React application. Although DataTables library is widely used with jQuery, our implementation in React application was devoid of jQuery. We have also been able to dynamically manipulate the generated table data using the API object that DataTables library exposes.

  • Understanding React useEffect Hook

    In React applications development, there are times when we may want some piece of code to be executed after a component has been rendered. For example, we may want some configuration settings loaded after the application starts execution. In another example, we may want to run some piece of code in response to a change in a specific state variable.

    To execute some code logic outside React’s virtual DOM, we need to wait until React has rendered the component, and then invoke useEffect hook to run a function that executes the side effect code.

    In this post, we will examine side effects, and the need to invoke useEffect hook to asynchronously run a function to execute a side effect code. We will also consider how we can control the execution of side effect code with dependencies.

    Table of Contents

    What are Side Effects?

    React, as you may know, is a javascript library for building user interfaces. It is used for building visual elements in web and mobile applications. However, many useful applications are not complete with only UI elements. They may interact with resources that are outside of UI presentation.

    For example, applications may need to interact with resources such as files, browsers DOM, localStorage, fetch data from databases and external API, etc. Such interactions with resources outside React are referred to as side effects. Such application codes outside React’s UI presentation are executed with useEffect hook.

    useEffect Runs Side Effect Function

    As ealier indicated, a React application may need to interact with resources outside of the UI code. We have referred to such external interactions as side effects. For any React component, if there is the need to execute side effects, we need to inform React about the function that needs to be called to interact with external resources.

    React useEffect hook is used to call a function that executes outside of React’s virtual DOM. Thus, useEffect hook is used to call a function that runs side effect code. It accepts a mandatory function as the first argument and an optional dependency array as second argument. The code inside the function passed to the useEffect hook is the side effect code we intend to run. It has the following signature:

    JavaScript
    // useEffect function signature
    useEffect(side_effect_function, [optional_dependency]);

    The function which is called to execute side effect code can be a named function or an anonymous function. Executing side effect code with anonymous function is the most common practice. For example, the following code passes an anonymous function to useEffect:

    JavaScript
    // execute side effect code with anonymous function
    useEffect(() => {
        document.title = 'Understanding useEffect Hook';
    });

    The alternative call, which is less common, is to pass the name of the function that executes side effect code. An example is shown below:

    JavaScript
    // execute side effect code with named function
    useEffect(setTitle);
    
    // side effect function
    function setTitle() {
        document.title = 'Understanding useEffect Hook';
    }

    From the above code, it can be observed that setTitle() interacts with the browser’s document object. This document object is outside of React’s UI presentation. Since this is a side effect, we execute setTitle() with a useEffect call on line 2.

    Parts of Side Effect Function

    A side effect function, which is run by useEffect hook, has two parts. These are the side effect code and the cleanup code. The parts of the side effect function are explained below:

    1. Side Effect Code: This is the actual side effect code that is run by the function. It may include tasks such as fetching data from an API, connecting to a database, manipulating the browser’s DOM, interacting with the browsers localStorage, etc. In sum, this is the main task that the side effect function performs.
    2. Cleanup Code (Optional): This is the code to be executed when the component is about to be unmounted from the DOM. For example, if the side effect code performs tasks which involve allocating memory, registering events, or other resource usages, then this is the part where cleanup must be done. This part is where any allocated memory must be freed, registered events must be unregistered, etc.

    Within the side effect function, the side effect code is mainly the code in the body of the side effect function excluding a return statement.

    JavaScript
    useEffect(() => {
        /*
         * the side effect code for the function somewhere here.
         * This is the part of the code with return statement excluded
         */
         document.title = 'Hello React';
    });

    From listing 4, the side effect code is the code in the body of the anonymous function passed to the useEffect hook. This is the actual task that we want the function to perform. In this example, the function sets the title of the HTML document. The side effect code does not include a return statement.

    If there is the need to perform any cleanup, it must be done by another function. For React to know that a cleanup will need to be performed before the component unmounts, we will need to return the function that performs the cleanup from the side effect function passed to useEffect.

    From the code in listing 5 below, the function called to perform side effect returns a function that will need to be called to perform cleanup:

    JavaScript
    useEffect(() => {
        // the side effect code for the function somewhere here
        
        /* when the component is about to be unmounted, the
         * return function below will be called to perform
         * any cleanup before component unmount is done
         */
        return () => {
            // perform cleanup, such as unregistering events, release 
            // of resources created and used in side effect, etc.
        };
    });

    We can observe that there is a return statement on line 8. As can be seen, line 8 returns a function. This is a cleanup function. When present, React will remember this function but will not call it immediately. This cleanup function will be called to perform any cleanup of resources allocated when React is about to unmount the component within which the side effect was performed.

    Just as a return statement is optional in any javascript function, the cleanup function is also optional. This is because not all side effect codes use memory allocating resources or subscribe to some events. Such side effect codes do not require cleanup. In such cases, there is no need to return a cleanup function..

    Since a cleanup function is optional, it implies that we can write a side effect code without returning a cleanup function. For example:

    JavaScript
    useEffect(() => {
        // interacting directly with the browser document is side effect
        document.title = 'React Applications Development';
    });

    Considering the above code, no cleanup is required to be done after the execution of the side effect code. Due to this, we do not return any cleanup function.

    When Does useEffect Execute?

    A useEffect hook is always executed when the component first mounts (first render), and optionally on component updates (re-render). This implies that a useEffect hook will be invoked at least once.

    The first execution occurs after the component first renders. Whether it will execute again or not depends on some specified props and or state variables defined as the second argument, a dependency array.

    To better understand when a useEffect hook usage, we will look at when side effect code executes and different ways in which we can control its subsequent execution.

    1. Component Mount and All Updates

    By default, a useEffect will always run when the component is rendered for the first time, and subsequently execute whenever the component updates. In other words, it will execute on all component renders, whether on first render (mount) or re-renders (updates). This is the default uncontrolled behaviour.

    To execute a useEffect hook anytime on component mount and component updates, we will need to pass only the first argument, which is the side effect function, to useEffect hook. In this case, the second argument, which is the dependency array, should not be passed.

    JavaScript
    useEffect(side_effect_function);

    As can be seen, we are only passing the side effect function as the first argument without passing the second argument to useEffect. An example is shown below:

    JavaScript
    useEffect(() => {
        // side effect code here
    });

    The following code is a working example of useEffect hook that runs a side effect function when the component is first mounted and when the component is updated.

    JavaScript
    // perform necessary imports
    import React, {useState, useEffect} from "react";
    
    const App = () => {
        // state variable for heading text
        const [heading, setHeading] = useState('Sample Text');
    
        // call the argument function to set the document title
        // when this App component first mounts in the DOM and 
        // anytime the App component is updated
        useEffect(() => {
            // set the title of the HTML document
            document.title = heading;
        });
    
        return (
            <div style={{textAlign: 'center'}}>
                <h2>{heading}</h2>
                <hr />
    
                <div style={{marginTop: '20px'}}>
                    <label>Enter Text: </label>
                    <input type='text' onChange={(e) => setHeading(e.target.value)} />
                </div>
            </div>
        )
    }
    
    export default App;

    We have said that a useEffect hook will always run at least once: when the component in which it is contained is mounted for the first time.

    Since a useEffect hook is executed after component mount, the HTML document title is set to the heading state variable, which by then has the value 'Sample Text'. Hence the title of the HTML document will be set to Sample Text when the document load is completed.

    We have also indicated that subsequent execution of side effect code depends on the second argument passed to useEffect. Since we did not pass the second argument to the useEffect call, the side effect code will always be executed when the component updates.

    On line 23, in listing 9, an onChange event handler, setHeading, updates state variable heading as we enter text in the input control. The heading state variable is also set to the <h2></h2> tag on line 18, causing the App component to update.

    An update to the App component triggers the useEffect call to set the title of the document to the state variable heading. The result is that whenever we type in the input control, the entered text is updated in the h2 heading text, and is subsequently set as the HTML document title.

    useeffect

    As we type in the input control, the App component is re-rendered to update the heading text. However, because we did not pass any dependency value to useEffect, the side effect code is executed whenever the App component updates. Thus, the change in the heading text is also reflected in the change in the title of the HTML document.

    2. Component Mount and Conditional State / Prop Update

    Recall that the second argument passed to useEffect is a dependency information. It tells that, after the first execution on mount, any further execution of the side effect code depends on some condition or state of some specified variables. React allows us to specify multiple states or props which, when they change, trigger further execution of the side effect code.

    We can specify the dependent state or prop variables as an array and pass this dependency array as the second argument to useEffect hook call:

    JavaScript
    useEffect(side_effect_function, [dependency_state_variables]);

    In the above code, we have specified the second argument to useEffect as an array. The dependency array contains the state or prop variables that the side effect code will depend on for further execution. For example:

    JavaScript
    useEffect(() => {
        // side effect code here
    }, [var1]);

    After the first execution of the side effect code on component mount, further execution will only occur when var1 changes.

    If there are multiple state or prop variables that the side effect code will depend on for further execution, they will need to be separated by comma in the array delimiter:

    JavaScript
    useEffect(() => {
        // side effect code here
    }, [var1, var1, var3]);

    From the above code listing, we are actually saying that, after the first execution on component mount, the side effect code should be executed again when a change occurs in either var1, var2, or var3. Thus, further execution of the side effect code depends on changes in the state or prop variables specified in the dependency array.

    To demonstrate with an example, we will consider one similar to the previous demonstration. Rather than update the title of the HTML document as we type in the input control, we will rather update only the content heading. The HTML document title will be updated to the entered heading text after a button is clicked.

    JavaScript
    // perform necessary imports
    import React, {useState, useEffect} from "react";
    
    const App = () => {
        // state variable for heading text
        const [heading, setHeading] = useState('Sample Text');
        const [canSetTitle, setCanSetTitle] = useState(false);
    
        // call the argument function to set the document title
        // when this App component first mounts in the DOM and 
        // anytime the App component is updated
        useEffect(() => {
            // set the title of the HTML document
            document.title = canSetTitle ? heading : document.title;
        }, [canSetTitle]);
    
        const onInputChange = (e) => {
            setHeading(e.target.value);
            setCanSetTitle(false);
        }
        
        return (
            <div style={{textAlign: 'center'}}>
                <h2>{heading}</h2>
                <hr />
    
                <div style={{marginTop: '20px'}}>
                    <label>Enter Text: </label>
                    <input type='text' onChange={onInputChange} />
                    <button onClick={() => { setCanSetTitle(true) }}>Set Title</button>
                </div>
            </div>
        )
    }
    
    export default App;

    On line 7, we declare boolean state variable canSetTitle, which we will use to determine whether we can set the title of the HTML document or not. Since this is a side effect, we execute with a useEffect call on line 12.

    The side effect code will be executed when the App component first mounts. However its subsequent execution will be done when canSetTitle state variable changes. This is because we have passed canSetTitle state variable as a dependency in the array passed as second argument to the the useEffect call.

    As we type in the input control, the onInputChange event handler is called to set the content heading. This time, the document title is not set automatically. The onInputChange event handler rather sets canSetTitle state variable to false.

    A click on the button updates canSetTitle to true. This update in the state variable, set as a dependency on line 15, causes the side effect code to execute. If canSetTitle is true, then the document title is set to the heading text. However, if canSetTitle is false, the title is maintained.

    As we type in the input control, the content heading updates. However, the title of the HTML document does not update until the button is clicked. This is because the side effect code which updates the HTML document title depends on changes in the state variable canSetTitle.

    3. Component Mount Only

    Sometimes, we may want to execute side effect code only once when a component is mounted (first render). In this case, we wouldn’t want the side effect code to be executed any further. For example, after an App component is mounted, we may want to load some configuration settings from an external server. Once the configuration settings are loaded, we may not want to load the same configuration settings again just because there is a UI update.

    To execute a side effect code only once, we will need to pass an empty array as the second argument to the useEffect hook call:

    JavaScript
    useEffect(() => {
        // side effect code here
    }, []);

    The side effect code will be executed when the component is first mounted. However, because the dependency array is empty, there wouldn’t be any prop or state variable to trigger further execution. The result is that the side effect code will never be executed again.

    The following code listing is a slight update to listing 13 seen earlier. For brevity, let’s see line 12 to line 15 in listing 13, shown below:

    JavaScript
    useEffect(() => {
        // set the title of the HTML document
        document.title = canSetTitle ? heading : document.title;
    }, [canSetTitle]);

    The only update we will do is to clear canSetTitle from the dependency array. This will mean that we will have an empty array as the second argument to the useEffect hook call.

    JavaScript
    useEffect(() => {
        // set the title of the HTML document
        document.title = canSetTitle ? heading : document.title;
    }, []);

    Every other part of listing 13 must be left intact. The complete code after the performing the update is shown below:

    JavaScript
    // perform necessary imports
    import React, {useState, useEffect} from "react";
    
    const App = () => {
        // state variable for heading text
        const [heading, setHeading] = useState('Sample Text');
        const [canSetTitle, setCanSetTitle] = useState(false);
    
        // call the argument function to set the document title
        // when this App component first mounts in the DOM and 
        // anytime the App component is updated
        useEffect(() => {
            // set the title of the HTML document
            document.title = canSetTitle ? heading : document.title;
        }, []);
    
        const onInputChange = (e) => {
            setHeading(e.target.value);
            setCanSetTitle(false);
        }
        
        return (
            <div style={{textAlign: 'center'}}>
                <h2>{heading}</h2>
                <hr />
    
                <div style={{marginTop: '20px'}}>
                    <label>Enter Text: </label>
                    <input type='text' onChange={onInputChange} />
                    <button onClick={() => { setCanSetTitle(true) }}>Set Title</button>
                </div>
            </div>
        )
    }
    
    export default App;

    Compared to listing 13, the code listing above has an empty array passed to the useEffect hook call on line 15. With empty array dependency, the side effect code will not execute again after its first execution on component mount.

    useeffect-mount-only

    As we type in the input control, the content heading updates. However, the document title does not change after clicking on the button. This is because the useEffect hook call has an empty dependency array. Therefore, the side effect code does not execute when there is component update (re-render).

    Summary

    A React application may interact with code or resources that are outside React’s UI presentation. Such interactions are known as side effects. They include interactions such as connection with external API, browser DOM, localStorage, etc.

    To execute a side effect code, the React useEffect hook is invoked to run the function that executes side effect code. The side effect function may optionally return another function that will be called to perform side effect cleanups when the component is about to be unmounted from the DOM.

    React useEffect will always run a side effect function when the component is first mounted in the DOM. Its subsequent execution depends on a second argument passed to useEffect. This second argument is specified as an array of state or prop variables whose updates trigger the further execution of the side effect code.

    If an empty array is passed as dependency, then the useEffect hook call will never be run again. If state or prop variables are specified, then the side effect code will execute when any of the specified state or prop variables are updated.

  • How To Add Class Names to Element with Javascript

    Javascript is a web programming language that can be used to dynamically update the content of a web page. One of the many, many, dynamic tasks that can be performed with javascript is adding class names to HTML element. Dynamic manipulation of element classes provide a more dynamic web content views.

    In this post, we will learn different approaches through which we can dynamically add class names to HTML element using javascript. The approach that you choose to use is matter of taste and convenience. We will take a step by step approach to discuss each method for a better understanding.

    Table of Contents

    In the discussions that follow, we will assume the following HTML document in which we have defined three CSS styles.

    HTML
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    
        <style type="text/css">
            .fs-40 {
                font-size: 40px;
            }
    
            .italic {
                font-style: italic;
            }
    
            .fw-700 {
                font-weight: 700;
            }
        </style>
    </head>
    <body>
        <div id="test">Apply CSS styles dynamically with javascript</div>
        
        <script src="index.js"></script>
    </body>
    </html>

    We will apply the CSS styles with classes .fs-40, .fw-700, and .italic to the div element dynamically using javascript. As indicated earlier, we will consider three different ways through which we can style the div element using javascript. I will assume you are loading a script file with name index.js, as shown in line 25 of the HTML document. You can however choose to write your javascript code inline in the same HTML document file.

    Add Class with classList Object

    The first method that we will use to add a class name to HTML element dynamically is the add() method of classList object. The classList object maintains a list of class names for an HTML element. Having the class names of an element as a list provides an easy interface to remove or add additional class names to the element. We can add new class names to an element by calling the add() method of the classList object on the HTML element.

    Using the HTML document shown earlier, let’s see how we can add class names to the div element using javascript.

    JavaScript
    // get reference to the div element
    const div = document.getElementById("test");
    
    // add new class to the element
    div.classList.add("fs-40");

    In the above code, we obtain a reference to the div element on line 2. On line 5, we use the classList object of the div element to call add() by specifying the class name fs-40. This applys a font-size: 40px to the div content. The result is shown in the following image:

    fs-40

    To add multiple class names in a single call, we will need to pass a comma-separated class names to the add() method:

    JavaScript
    // get reference to the div element
    const div = document.getElementById("info");
    
    // add new class to the element
    div.classList.add("fs-40", "italic");

    The above code applies a font-size: 40px and font-style: italic to the div element text with class names .fs-40 and .italic respectively. The result is shown below:

    fs40 fs-italic

    As can be seen, the div element text has italic font style applied with a font size of 40px as defined in the document style specification.

    Thus, we can use javascript to add class names to HTML elements by calling add() method of the classList object. We invoke classList object with the HTML element that we want to apply the styles.

    Add Class with className Property

    The className property of an element is a getter and a setter property that is used to get and set the class attribute of an element respectively. This implies that we can use className property to set a class name and also use it to get the class names that have been specified on an element. As className is of type string, we can add additional class names by concatenating the className property with new class names. Let’s look at how we set and add class names to an element using the className property.

    We will begin by resetting our javascript code so that no styles are applied to the div element text. A preview of the document as viewed in the browser without any styles applied is shown as follows:

    no-styles-text

    To set a class name on an element, we only need to set the name of the class that we want to apply by setting it to the className property. An example is shown in the following javascript code:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.className = 'fs-40';

    A view of document after adding the class name fs-40 is shown in the following image:

    className

    As indicated earlier, the className property is of type string. We can therefore add additional class names by concatenating the new class names to the className property.

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.className = 'fs-40';
    
    // add the italic style to the existing style
    div.className += ' italic';

    When adding additional class names with string concatenation, it is helpful to include a space before the new class names being appended. Observe from line 8 that there is a space before the class name that is being append. That is: " italic". With className already containing fs-40, the preceding space character results in the className property to contain the value "fs-40 italic". If we omit the initial space before the new class name we want to add, then className will contain the value "fs-40italic" which isn’t what we want. In this case, we will even lose the style for the last added class name.

    A view of the document with class names fs-40 and italic added is shown as follows:

    fs-40 fs-italic

    It is not necessary to first set an initial class name and later append remaining class names with className property. In fact, we can apply string concatenation in our first step when no class names have initially been set. The result will be the same:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px and apply italic font style
    div.className += ' fs-40 italic';

    Add Class with setAttribute Method

    Another approach to set or update the class attribute of an element is to call setAttribute() method of the target element. This is done by specifying class as first argument, and the class names as the second argument.

    JavaScript
    // get the div element
    const div = document.getElementById('test');
    
    // set the class name with setAttribute
    div.setAttribute('class', 'fs-10');

    We are not restricted to setting single class names at a time with setAttribute(). We can set multiple class names in a single call to setAttribute() method:

    JavaScript
    // get the div element
    const div = document.getElementById('test');
    
    // set the class name with setAttribute
    div.setAttribute('class', 'fs-10 italic fw-700');

    Setting class names with setAttribute() method will override any class names that were initially set. In order to append to existing class names that were previously set, we will need to get the existing class names by calling getAttribute() method, and then concatenate the return value with the additional class names we want to append.

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.setAttribute('class', 'fs-40');
    
    // add the italic and font weight styles to the existing style
    div.setAttribute('class', div.getAttribute('class') + ' italic fw-700');

    If you are a lover of javascript template strings, that will also do fine, and the result will be the same:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.setAttribute('class', 'fs-40');
    
    // add the italic and font weight styles to the existing style
    div.setAttribute('class', `${div.getAttribute('class')} italic fw-700`);

    Conclusion

    Using the javascript programming language, we can dynamically apply CSS styles to HTML elements for dynamic web views. There are different ways through which this can be accomplished.

    HTML elements contain the classList object which provides methods for adding and removing CSS classes. The classList object contains other methods for manipulating element styles. To add a class name to an element, we call the add() method of the classList object. We can add multiple class names in a single call by passing comma-separated class names to the add() method:

    JavaScript
    // get reference to the div element
    const div = document.getElementById("info");
    
    // add new class to the element
    div.classList.add("fs-40");
    
    // add multiple classes in a single call
    div.classList.add("fs-40", "italic");

    Another approach that can be used to dynamically add class names with javascript is to set or append to the className property of the HTML element:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.className = 'fs-40';

    When appending to existing class names with className property, it is important to precede the new class names with a space. This is to avoid appending the new class name to the last added class name with no space between them:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.className = 'fs-40';
    
    // add the italic style to the existing style
    div.className += ' fw-700 italic';

    Moreover, we can use javascript to dynamically add class names to an element by calling the setAttribute() method of the HTML element. With this method, we specify the attribute name as class by passing it as the irst argument, and then pass the class names that we want to set as the second argument:

    JavaScript
    // get the div element
    const div = document.getElementById('test');
    
    // set the class name with setAttribute
    div.setAttribute('class', 'fs-10 fw-700');

    If we need to add to existing class names, we can get the existing class names by calling getAttribute(), and then append the new class names that we want to add:

    JavaScript
    // get the div element
    const div = document.getElementById("test");
    
    // set the size of the text to 40px
    div.setAttribute('class', 'fs-40');
    
    // add the italic and font weight styles to the existing style
    div.setAttribute('class', div.getAttribute('class') + ' italic');
  • Clear and Replace Children of Element with Javascript

    In web applications development, some HTML elements can contain one or more child elements. Using javascript, the children of HTML element can be modified to suit the current view or application data. Sometimes, it becomes necessary to clear the entire children of an HTML parent element, and or replace them with a different set of child elements.

    In this post, we will learn to know how we can clear children of a parent object, and how we can also replace them with a different set of child elements. We will consider different methods that can be used to accomplish this task.

    To have a better understanding, we will also take a practical approach, with example, to demonstrate what we discuss in this post. We will first explain the methods that can be used to clear and replace children of a parent object, and then look at a working example that demonstrates how we can accomplish this task.

    Clear Children with replaceChildren()

    The simplest approach to clear children of a parent element is to call replaceChildren() method of the parent object without supplying any values to the method call. replaceChildren() accepts an infinite number of Node and string objects which can be provided to the method to replace the existing child elements. If we do not provide any values to replaceChildren() in its call, then it will simply clear the existing list of child items for the calling parent object.

    Suppose we have the following HTML extract:

    HTML
    <ul id="list">
        <li>HTML</li>
        <li>CSS</li>
        <li>Javascript</li>
    </ul>

    We can clear all li items with replaceChildren() when we have a reference to the parent ul element:

    JavaScript
    // get the ul element
    const ul = document.getElementById('list');
    
    // To clear all items, don't provide any replacement nodes
    ul.replaceChildren();

    Since no Node or string objects are supplied to replaceChildren() method, there will be no items to replace the cleared ones. The effect is that replaceChildren(), without any argument values provided, will clear the entire set of child elements of the parent object without replacement.

    Clear Children with removeChild()

    An alternative method to clear children of a parent object is to call removeChild() method for each child element. This approach works by iterating through the children of the parent object, and removing each child element one at a time in the iteration. We can use a while loop condition to check if the parent object contains some child objects. This is done with a call to hasChildNodes(). If a child item exists, then we call removeChild() by passing to it either the firstChild or lastChild property of the parent object.

    JavaScript
    // get the ul element
    const ul = document.getElementById('list');
    
    // check to see if child items exist, if so call to remove each child that exists
    while (ul.hasChildNodes())
        ul.removeChild(ul.firstChild);

    The while loop condition tests if the parent element contains some child items. As long as a child exists, hasChildNodes() will return true, and the first child item will be removed. When all child items have been removed, hasChildNodes() will return false, and the while loop will terminate.

    If we want to remove the items from the bottom, we can pass lastChild property of the parent object to removeChild(). The result will be the same. All child items will be removed from the parent object.

    JavaScript
    // check to see child items exist, if so call to remove each child that exists
    while (ul.hasChildNodes())
        ul.removeChild(ul.lastChild);

    From line 3, we pass lastChild property of the parent object, which is ul, to removeChild() to remove child items from the bottom.

    Replace Children with replaceChildren()

    To replace children of an element with new set of child elements, we will need to call replaceChildren() method and provide it with an unlimited number of child items that will replace the existing ones.

    Suppose in listing 1, we want to replace all li items with new ones. The following javascript code demonstrates how we can call replaceChildren() with new child items for replacement.

    JavaScript
    // create list items
    const li1 = document.createElement('li');
    const li2 = document.createElement('li');
    const li3 = document.createElement('li');
    
    // set text for each list item
    li1.innerText = 'PHP';
    li2.innerText = 'Javascript';
    li3.innerText = 'Python';
    
    // get the ul element
    const ul = document.getElementById('list');
    
    // replace with new list items
    ul.replaceChildren(li1, li2, li3);

    Like appendChild(), replaceChildren() also accepts unlimited Node and string objects as arguments. Hence, we can also pass string objects as argument to replaceChildren() method call.

    JavaScript
    // replace with new list items
    ul.replaceChildren(li1, li2, li3, 'Laravel');

    Observe that the last item passed to replaceChildren() in the previous code is a string object. As indicated earlier, replaceChildren() accepts both Node and string objects as argument. Additionally, there is no limit to the number of items that can be passed to it.

    With our current understanding of how to clear children and replace children of a parent object, we can take a practial example to broaden our understanding of the discussions so far.

    Practical Example

    In this practical example to clear and replace children of a parent object, we will randomly generate some integer values as list items. We will introduce two buttons: one to clear the child items without replacement, and another to replace the child items. We will do this dynamically with javascript.

    Create HTML document file and give it any name of your chossing, such as index.html. Also, create a javascript file in which we will define our javascript functions. You can give it any name, for example index.js.

    Copy and paste the following content into index.html file. You should notice that we are also loading the javascript file that was created, in my case index.js, on line 24.

    HTML
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Clear and Replace Element Children</title>
    </head>
    <body>
        <div class="container">
            <ul id="list">
                <li>34</li>
                <li>28</li>
                <li>46</li>
                <li>17</li>
            </ul>
    
            <hr />
            <div class="controls">
                <button onclick="clearChildren()">Clear</button>
            </div>
        </div>
    
        <!-- load the javascript file -->
        <script type="text/javascript" src="./index.js"></script>
    </body>
    </html>

    The content of the HTML is very clear. We are rendering an unsorted list of some random numbers. We can identify that the onclick event of the button is set to clearChildren(). We will define this function in the javascript file shortly. When the button is clicked, clearChildren() will be called to clear all child items of the ul element.

    If you preview the HTML document file in a web browser, you should see a page similar to the following:

    Let’s see how we can separately clear and replace list items of the parent object.

    Clear Children of Parent Element

    In listing 7, we have set the onclick event of the button for clearing the list items to clearChildren(). Let’s define this function in the javascript file. We will call replaceChildren() in the function definition to clear all the list items of the ul element. Remember that a call to replaceChildren() method without any arguments will clear the existing child items with any replacement.

    JavaScript
    // get reference to the ul element
    const ul = document.getElementById('list');
    
    function clearChildren() {
        // clear the list items with no replacement
        ul.replaceChildren();
    }

    With clearChildren() defined in the javascript file, go ahead and click on the Clear button. You will notice that the list of numbers will disappear. You can see the result below:

    clear children

    The call to replaceChildren() cleared all the child items of the ul element. Since we did not provide any values to replaceChildren(), no new list items were used to replace the ones that were cleared.

    To keep on playing with it, refresh the page to have the initial page rendered. You can then continue to click on the Clear button to see it working.

    Replace Children of Parent Element

    To see how we can replace children of a parent object with new items, we will introduce a new button element. We will label this button as Replace. When this button is clicked, we will generate random numbers from 10 to 50 which we will use to create Element objects of li to replace the existing child items.

    Within <div class="controls"></div>, update the HTML to include the new button element for replacement:

    HTML
    ...
    
    <div class="controls">
        <button onclick="clearChildren()">Clear</div>
        <button onclick="replaceChildren()">Replace</div>
    </div>

    You can see the introduction of the new button at line 5. We have set the onclick event of this new button to replaceChildren(). This replaceChildren() does not yet exist in the javascript file. We will define it shortly.

    replace children button

    In addition to replaceChildren() function, we will also need another function that will create li items of random integer numbers.

    In the javascript file, add two more functions as replaceChildren() and getRandomItems() with their implementations as shown below:

    JavaScript
    ...
    
    // function to replace list items with new ones
    function replaceChildren() {
        // get the random list items
        const randomList = generateRandomItems();
    
        // replace with the new items
        ul.replaceChildren(...randomList);
    }
    
    // function to create li items of random numbers
    function getRandomItems() {
        const count = 4, min = 10, max = 50;
        const items = [];
    
        for (let i = 0; i < count; i++) {
            let li = document.createElement('li');
            li.innerText = Math.floor(Math.random() * (max - min) + min);
    
            items[i] = li;
        }
    
        // return the generated list items
        return items;
    }

    Our main interest is in replaceChildren() function. As can be seen, a call to getRandomItems() is made to generate and return an array of randomly generated li items. Since an array is iterable, we are able to pass the returned value, in this case randomList, to replaceChildren() method of the ul element using the spread syntax.

    Any time the Replace button is clicked, you will notice that the list of numbers rendered on the page are replaced with a new set of numbers. This is done with the call to replaceChildren() on line 9 where we pass that list items using the spread syntax. The following image shows the result of clicking on the Replace button. Understand that the values will not necessarily be the same as yours since they are randomly generated.

    replace children

    Thus, to replace children of a parent object, we need to call replaceChildren() method of that parent object and provide it with a new set of values for replacement. Continue clicking on the Replace button to see that the list gets replaced with a new set of integer values.

    Summary

    In this post, we have seen how we can clear children of a parent object as well as how to replace children of a parent object.

    To clear children of a parent object, we need to call replaceChildren() method of the parent object without passing any values to the method call. replaceChildren() will clear the existing child items when called.

    JavaScript
    // get reference to the ul element
    const ul = document.getElementById('list');
    
    // To clear all items, don't provide any replacement nodes
    ul.replaceChildren();

    Since no new values are provided to the method when called, there will be no new set of values to be used for replacement.

    Another approach to clear children of a parent object is to remove the child items one at a time with removeChild() method of that parent object. Using a while loop, we can check if any child exists. If a child exists, we can pass either firstChild or lastChild properties to removeChild() method of the parent object.

    JavaScript
    // get reference to the ul element
    const ul = document.getElementById('list');
    
    while (ul.hasChildNodes())
        ul.removeChild(ul.firstChild);

    If we want to replace child elements of a parent object with new items, we need to call replaceChildren() method and provide it with the new set of items that will be used to replace the existing child items:

    JavaScript
    // get the ul element
    const ul = document.getElementById('list');
    
    // replace with new list items
    ul.replaceChildren(li1, li2, li3);
  • Differences Between Javascript append() and appendChild()

    Javascript append() and appendChild() methods are commonly used to add child objects such as elements or text at the end of the children of a parent object. At certain times, both append() and appendChild() can be used for the same task. At other times too, their functionalities differ, and one of these methods will be required over the other. This will depend on the type of item that is being added.

    It is very important, as a javascript developer, to know when you can use either append() or appendChild() for the same task, and when they need to be used differently. This will help to know which of the methods you will need to call when adding child items to an HTML element or node.

    Our main goal in this post is to understand the differences between append() and appendChild() methods in javascript. However, we will also consider the way in which they are similar. After going through the discussion in this post, we will realise that append() can do what appendChild() does, but the opposite is not so. appendChild() cannot do all that append() does, leading us to understand the differences between them.

    Table of Contents

    appendChild()

    The appendChild() method is defined in the Node interface, an abstract base class from which other types are derived. This method adds an object of type Node as the last item to the children of a parent Node object. After adding the specified item to the list of children, the method returns the same Node object that was added.

    appendChild() accepts only one Node item in its method signature. This means that you can only add one item at a time with this method. We will see practical usages of this method shortly.

    append()

    The append() method is defined in the Element class. This method adds an object of type Node or string as the last item of the children of a parent Node or Element object. In other words, append() can add an object of type Node as well as a string object as last child. But recall that appendChild() also adds an object of type Node as the last child. This implies that append() does what appendChild() does, but extends further to accept and add string objects as well.

    Unlike appendChild() which adds only a single Node item at a time, append() can be used to add more than one objects to the children of a parent Node. Additionally, this method allows adding string objects as well.

    Relationship Between Element and Node

    To better understand the differences between append() and appendChild(), it will be very helpful to first understand the relationship that exists between Element and Node types in javascript.

    In javascript, the Node type is an abstract base class in which appendChild() is defined. appendChild() is defined to accept and add a single object of Node type, and returns the added item.

    Node.appendChild(Node)

    The Element class, on the other hand, is derived from the Node abstract class. Since Element derives from Node, it implies that when we create an Element object such as div, span, p, etc, that object is also of type Node, and therefore automatically inherits the appendChild() method defined in the Node interface. Hence, we can say that an Element object is also a Node object, and can therefore call the appendChild() method.

    JavaScript
    // create a div Element object
    let div = document.createElement('div');
    
    // create a p Element object which will be appended
    let p = document.createElement('p');
    p.innerText = 'An Element object is also a Node object.';
    
    // Since div is an Element object, it is also a Node object
    // Therefore, it can call appendChild() to add child items
    div.appendChild(p);

    If you log div in your browser console with console.log(div), you should see the following DOM tree:

    Although an Element object can use appendChild() method to add child items to its children, it also defines the append() method which can perform the same task that appendChild() does. In listing 1, if we change the appendChild() method call on line 10 to append(), you will get the same result when you log div with console.log(div) in your browser.

    JavaScript
    // Calling append() with an Element object produces the same
    // result as calling appendChild() with the Element object
    div.append(p);

    As indicated earlier, calling div.append(p) or div.appendChild(p) in listing 1 above will produce the same result. So, if both methods can perform the same task, then what sets them apart? Let’s find the differences between them.

    Differences Between append() and appendChild()

    The differences between append() and appendChild() lies in the type of objects that they can add, the number of objects they accept in a single call, and their return value. We will look at their differences based on these three aspects.

    1. Parameter Types

    Let us consider the differences between append() and appendChild() by considering the type of objects that can be passed to these methods for adding items to children of a parent object.

    appendChild() Accepts Node Type Only

    In terms of method parameters, appendChild() only takes an object of type Node as an argument in its call. If the object given to appendChild() is not a Node type, then it will not be accepted. Recall that when you create HTML element, the Element object is also a Node object since Element class is derived from Node abstract class. Hence, we can pass HTML element to appendChild(). However, we cannot pass a string object to appendChild() since a string object is not a Node object.

    JavaScript
    let div = document.createElement('div');
    let p = document.createElement('p');
    p.innerText = 'An Element object is also a Node object.';
    
    // passing an Element p to appendChild() is accepted
    div.appendChild(p);
    
    // Initialise text as string object.
    let text = 'appendChild() accepts only objects of type Node.';
    
    // passing a string object to appendChild is not accepted.
    // This is because text is not a Node type but rather string
    div.appendChild(text);    // this should fail with an error

    In listing 3, p is initialised as an Element object. Since Element derives from Node, it implies that p is also a Node object. Therefore, we can pass p as argument to appendChild() on line 7.

    On line 10, we initialise text as a string object. Since text is not a Node object, passing it as an argument to appendChild() on line 14 will fail and produce an error. For instance, in Google Chrome browser, you should see an error message in your console similar to the following:

    To be able to add text with appendChild(), we will first need to create a Node object for the text by calling createTextNode(), and then pass it as argument to appendChild():

    JavaScript
    ...
    // Initialise text as string object.
    let text = 'appendChild() accepts only objects of type Node.';
    
    // create Node object for text
    let textNode = document.createTextNode(text);
    
    // passing textNode is accepted since it is a Node object
    div.appendChild(textNode);

    Since textNode is of type Node, appendChild() will gracefully accept it.

    append() Accepts Node Type and string Type

    Like appendChild(), the append() method also accepts objects of type Node to be added as child items. However, it can also accept objects of type string. In this case, there is no need to create a Node for the text.

    JavaScript
    let div = document.createElement('div');
    let p = document.createElement('p');
    p.innerText = 'An Element object is also a Node object.';
    
    // passing an Element p to append() is accepted
    div.append(p);
    
    // Initialise text as string object.
    let text = 'append() accepts objects of type string.';
    
    // passing a string object to append is accepted.
    div.append(text);

    As can be seen, the Element p and the the text have been added as children of the parent div with append() method.

    appendChild() takes only objects of type Node as argument. On the other hand, append() takes objects of type Node and can take string objects as well.

    2. Number of Parameters

    Another difference between append() and appendChild() methods is the number of items that can be passed to them to be added as children of the parent object.

    appendChild() Accepts Single Item

    When adding child items, you can only add one item or element at a time with appendChild(). This means that if you have a list of elements and you need to add to a parent object with appendChild(), then you will have to add them one at a time.

    JavaScript
    // create a list container
    let ul = document.createElement('ul');
    
    // create list items
    let li1 = document.createElement('li');
    li.innerText = 'List Item 1';
    
    let li2 = document.createElement('li');
    li2.innerText = 'List Item 2';
    
    let li3 = document.createElement('li');
    li3.innerText = 'List item 3';
    
    // With appendChild(), we will need to add one item at a time
    ul.appendChild(li1);
    ul.appendChild(li2);
    ul.appendChild(li3);

    append() Accepts Multiple Items

    When using append() method, we can pass multiple items to be added as children of a parent object in just a single call. This can be done by passing the elements to be added as a comma-separated list of items. For instance, in listing 6, we could just call append() and pass all the items to be added rather than add them one at a time with appendChild().

    JavaScript
    ...
    // add all the list items with append() in just a single call
    ul.append(li1, li2, li3);

    Do you remember that append() accepts string objects as well?. Then, you can even include string objects in the comma-separated list of items passed to append() method:

    JavaScript
    ...
    // add all items with append(), including string objects
    ul.append(li1, li2, li3, 'List item 4', 'List item 5');

    As can be seen, you can have a mix of elements and text in the comma-separated list of items as argument to append().

    appendChild() accepts and adds one item at a time. However, append() accepts and adds multiple items in a single call

    3. Return Value

    A third difference between append() and appendChild() is their return value after completing their task. Whiles appendChild() returns the item that was added, append() does not return a value. If you attempt to check the return value of append(), you will notice that it is undefined.

    JavaScript
    // create a list container
    let ul = document.createElement('ul');
    
    // create list items
    let li1 = document.createElement('li');
    li.innerText = 'List item 1';
    
    let li2 = document.createElement('li');
    li2.innerText = document.createElement('li');

    Using listing 9, let us add list items with both append() and appendChild() and see their return value.

    JavaScript
    // add list item with appendChild() and save return value
    let returnValue = ul.appendChild(li1);
    
    // log the return value
    console.log(returnValue);

    If you log the return value from appendChild() with console.log(returnValue), you should see the following output in your browser console:

    appendchild-return-value

    We can see that appendChild() returned the Node, in this case the Element object, which it was called to add.

    As indicated earlier, append() does not return a value after adding items. If we attempt to save and log return value from append() method, the result will be undefined.

    JavaScript
    // add list item with appendChild() and save return value
    let returnValue = ul.append(li2);
    
    // log the return value
    console.log(returnValue);

    Logging returnValue with console.log(returnValue) will produce the following in your browsers console:

    append-return-value

    This is a confirmation that append() does not return any value after adding specified items to children of a parent object.

    When appendChild() adds an object, it returns this same object after adding it. However, append() does not return any value after adding an object.

    Summary

    In javascript, append() and appendChild() are two methods that can be used to add items to children of a parent object. Depending on their usage, both methods can perform the same task. At certain times too, they have different usages which make one method different from the other.

    In terms of function parameters, appendChild() can be called to add only a Node object to children of a parent object. However, append() can take a Node object as well as a string object to be added to list of children of a parent object.

    When using appendChild(), only one object can be provided to be added to children of a parent object. However, with append() method, we can provide more than one Node or string objects to be added to children of a parent object in just a single call.

    When appendChild() is called to add an item, it returns the same object that it added after performing its task. On the other hand, append() does not return any value after performing its task.