Testing your apps like a boss with React.js and Jest 👏👏👏

Testing your apps like a boss with React.js and Jest 👏👏👏

When I started out writing tests for my React application, it took me some tries before I figured out how to set up my testing environment using React.js and Jest ...

When I started out writing tests for my React application, it took me some tries before I figured out how to set up my testing environment using React.js and Jest ...

In this article, we will cover the testing framework Jest. We will learn how to:

  • write tests, it’s a breeze to write tests and assert on specific conditions
  • manage our test suite, by running specific tests as well as specific test files by utilizing the pattern matching functionality
  • debug our tests, by augmenting VS Code we can gain the ability to set breakpoints in our tests and create a really nice debugging experience
  • snapshot mastery, learn how using snapshots can give you increased confidence that your components are still working after a change you made
  • leverage mocking, mocking dependencies can ensure you only test what you want to test and Jest has great defaults when it comes to mocking
  • coverage reports, we have come to expect a good coverage tool to be included in all good testing libraries. Jest is no different and it’s really easy to run coverage reports and quickly find what parts of your code that could benefit from some more testing

Jest sells itself by saying it’s

Delightful JavaScript testing
What makes is delightful? It boasts that it has a zero-configuration experience.

Ok, we are getting closer to the answer.

  • Great performance by tests running in parallel thanks to workers.
  • Built-in coverage tool
  • Works with typescript thanks to ts-jest
Get started

Let’s try to set it up and see how much configuration is needed. If you just want to try it, there is a Jest REPL where you will be able to write tests among other things.

Writing our first test

To make the test runner find the tests we need to follow one of three conventions:

  • Create a __tests__ directory and place your files in there
  • Make file match *spec.js
  • Make file match .test.js

Ok, so now we know how Jest will find our files, how about writing a test?

// add.js

function add(a, b) { 
  return a + b; 
} 

module.exports = add; 

// add.spec.js

const add = require('../add'); 
describe('add', () => { 
  it('should add two numbers', () => { 
    expect(add(1, 2)).toBe(3);   
  }); 
});

We see above that we are using describe to create a test suite and it to create a test within the test suite. We also see that we use expect to assert on the result. The expect gives us access to a lot of matchers, a matcher is a function we call after the expect:

expect(something).matcher(value)
As you can see in our test example we are using a matcher called toBe()which essentially matches what's inside the expect to what's inside the matcher, example:

expect(1).toBe(1) // succeeds 
expect(2).toBe(1) // fails

There are a ton of matchers so I urge you to have a look at the ones that exist and try to use the appropriate matcher Matchers

Running our test

The simplest thing we can do is just to create a project using create-react-app, cause Jest is already set up in there. Once we have the project created and all dependencies installed we can simply run:

yarn test

It will show the above image containing:

  • One executed test suite,
  • One passing tests and host of commands that we will explore in a bit. It seems to have run the file src/App.test.js.

Let's have a look at the said file:

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import App from './App'; 

it('renders without crashing', () => { 
  const div = document.createElement('div'); 
  ReactDOM.render(<App />, div); 
  ReactDOM.unmountComponentAtNode(div); 
});

As we can see it has created a test using it and have also created a component using ReactDOM.render(<App />, div), followed by cleaning up after itself by calling ReactDOM.unmount(div). We haven't really done any assertions at this point, we have just tried to create a component with no errors as the result, which is good to know though.

How about we try adding add.js file and its corresponding test?

Let’s first add add.js, like so:

// add.js

function add(a,b) { return a + b; } 
export default add;

followed by the test:

// add.spec.js

import add from '../add'; 

it('testing add', () => { 
  const actual = add(1,3); 
  expect(actual).toBe(4); 
});

Our Jest session is still running in the terminal:

We can see that we now have two passing tests.

Debugging

Any decent test runner/framework should give us the ability to debug our tests. It should give us the ability to:

  • run specific tests
  • ignore tests
  • set breakpoints, let us add breakpoints in our IDE (more up to the IDE vendor to make that happen)
  • run in browser, let us run our tests in a Browser

Run specific test files

Let us look at how to do these things, let’s start with running specific tests. First off we will add another file subtract.js and a corresponding test.

// subtract.js

function subtract(a,b) { 
  return a - b; 
} 
export default subtract;

and the test:

// subtract.spec.js

import subtract from '../subtract'; 

it('testing subtract', () => { 
  const actual = subtract(3,2); 
  expect(actual).toBe(1); 
});

Let’s have a look at our terminal again and especially at the bottom of it:

If you don’t see this press w as indicated on the screen. The above gives us a range of commands which will make our debugging easier:

  • a, runs all the tests
  • p, this will allow us to specify a pattern, typically we want to specify the name of a file here to make it only run that file.
  • t, it does the same as p but it lets us specify a test name instead
  • q, quits the watch mode
  • Return, to trigger a test run

Given the above description we will try to filter it down to only test the add.js file so we type p:

This takes us to a pattern dialog where we can type in the file name. Which we do:

Above we can now see that only the add.js file will be targeted.

Run specific tests

We have learned how to narrow it down to specific files. We can narrow it down to specific tests even using the p, pattern approach. First off we will need to add a test so we can actually filter it down:

//add.spec.js

import add from '../add'; 

it('testing add', () => { 
  const actual = add(1,3); 
  expect(actual).toBe(4); 
}); 

it('testing add - should be negative', () => { 
  const actual = add(-2,1); 
  expect(actual).toBe(-1); 
});

At this point our terminal looks like this:

So we have two passing tests in the same file but we only want to run a specific test. We do that by adding the .only() call to the test, like so:

it.only('testing add', () => { 
  const actual = add(1,3); 
  expect(actual).toBe(4); 
});

and the terminal now looks like so:

We can see that adding .only() works really fine if we only want to run that test. We can use .skip() to make the test runner skip a specific test:

it.skip('testing add', () => { 
  const actual = add(1,3); 
  expect(actual).toBe(4); 
});

The resulting terminal clearly indicated that we skipped a test:

Debugging with Breakpoints

Now, this one is a bit IDE dependant, for this section we will cover how to do this in VS Code. The first thing we are going to do is install an extension. Head over to the extension menu and search for Jest. The following should be showing:

Install this extension and head back to your code. Now we have some added capabilities. All of our tests should have a Debug link over every single test.

At this point, we can add a breakpoint and then press our Debug link. Your breakpoint should now be hit and it should look like so:

Snapshot testing

Snapshot is about creating a snapshot, a view of what the DOM looks like when you render your component. It’s used to ensure that when you or someone else does a change to the component the snapshot is there to tell you, you did a change, does the change look ok?

If you agree with the change you made you can easily update the snapshot with what DOM it now renders. So snapshot is your friend to safeguard you from unintentional changes.

Let’s see how we can create a snapshot. First off we might need to install a dependency:

yarn add react-test-renderer --save
Next step is to write a component and a test to go along with it. It should look something like this:

// Todos.js

import React from 'react'; 
const Todos = ({ todos }) => ( 
  <React.Fragment> 
   {todos.map(todo => <div>{todo}</div>)} 
  </React.Fragment> ); 
export default Todos;
// Todos.spec.js
import renderer from 'react-test-renderer'; 
import React from 'react'; 
import Todos from '../Todos'; 
test('Todo - should create snapshot', () => { 
  const component = renderer.create( 
    <Todos todos={['item1', 'item2']} /> 
  ); 
  let tree = component.toJSON(); 
  expect(tree).toMatchSnapshot(); 
})

Note how import, imports the component we are about to test:

import Todos from '../Todos';
This is followed by using the renderer to create an instance of our component. Next step is to turn that component into a JSON representation like so component.toJSON() and lastly, we assert on this by calling expect(tree).toMatchSnapshot(), this will call a snapshot that will place itself in a __snapshots__ directory under your tests directory.

Managing the snapshot

Ok, so we have a snapshot, now what? Let’s do a change to our todo component, like so:

// Todos.js

import React from 'react'; 
const Todos = ({ todos }) => ( 
  <React.Fragment> {
    todos.map(todo => ( 
      <React.Fragment> 
        <h3>{todo.title}</h3> <div>{todo.description}</div> 
      </React.Fragment> 
    ))}
   </React.Fragment> ); 

export default Todos;


We see that our todo is an object instead of a string so it has a title and description property. This WILL make our snapshot react and it will say the following:

It clearly indicates something is different and asks us to inspect the code. If we are happy with the changes we should press u to update the snapshot to its new version. So look at the code and yes this is an intended change so therefore we press u. We end up with the following image telling us everything is ok:

Mocking

Mocking is one of those things that needs to work well. Mocking in Jest is quite easy. You need to create your mocks in a directory that is adjacent to your module, or more like a child directory to the module. Let’s show what I mean in code. Imagine you have the following module:

// repository.js

const data = [{ title: 'data from database' }]; 

export default data;

Let’s look at a test for this one:

// repository.spec.js

import data from '../repository'; 

describe('testing repository data', () => { 
  it('should return 1 item', () => { 
    console.log(data); 
    expect(data.length).toBe(1); 
  }); 
});

Not the best of tests but it is a test. Let’s create our mock so that our file structure looks like so:

// directory structure

repository.js // our repo file
__mocks__/repository.js // our mock

Our mock should look like this:

// __mock__/repository.js

const data = [{ title: 'mocked data' }]; 
export default data;

To use this mock we need to call jest.mock() inside of our test, like so:


// repository.spec.js

import data from '../repository'; 
jest.mock('../repository'); // taking __mock/repository instead of the actual one
describe('testing repository data', () => { 
  it('should return 1 item', () => { 
    console.log(data); 
    expect(data.length).toBe(1); 
  }); 
});

Now it uses our mock instead of the actual module. Ok you say, why would I want to mock the very thing I want to test. Short answer is : you wouldn’t. So, therefore, we are going to create another file consumer.js that use our repository.js. So let's look at the code for that and its corresponding test:

// consumer.js

import data from './repository'; 
const item = { title: 'consumer' }; 
export default [ ...data, { ...item}];

Above we clearly see how our consumer use our repository.js and now we want to mock it so we can focus on testing the consumer module. Let's have a look at the test:

// consumer.spec.js

import data from '../consumer'; 
jest.mock('../repository'); 
describe('testing consumer data', () => { 
  it('should return 2 items', () => { 
    console.log(data); 
    expect(data.length).toBe(2); 
  }); 
});

We use jest.mock() and mocks away the only external dependency this module had.

What about libs like lodash or jquery, things that are not modules that we created but is dependant on? We can create mocks for those at the highest level by creating a __mocks__ directory.

There is a lot more that can be said about mocking, for more details check out the documentation Mocking docs

Coverage

We have come to the final section in this chapter. This is about realizing how much of our code is covered by tests. To check this we just run:

yarn test coverage
This will give us a table inside of the terminal that will tell us about the coverage in percentage per file. It will also produce a coverage directory that we can navigate into and find a HTML report of our coverage. But first off let's change the add.js file to add a piece of logic that needs a test, like so:

// add.js

function add(a, b) { 
  if(a > 0 && b > 0 ) { 
    return a + b; 
  } 
  throw new Error('parameters must be larger than zero'); 
} 
export default add;

Now we can see we have more than one path through the application. If our input params are larger than zero then we have existing tests that cover it.

However, if one or more parameters is below zero then we enter a new execution path and that one is NOT covered by tests. Let’s see what that looks like in the coverage report by navigating to coverage/lcov-report. We can show this by typing for example

http-server -p 5000
and we will get a report looking like this:

Now we can navigate to src/add.js and it should look like this:

Now we can clearly see how our added code is indicated in red and that we need to add a test to cover that new execution path.

Next, we add a test to cover for this, like so:

// add.spec.js

import add from '../add'; 
describe('add', () => { 
  it('testing addition', () => { 
    const actual = add(1,2); 
    expect(actual).toBe(3); 
  });

  it('testing addition with neg number', () => { 
    expect(() => { add(-1,2); }).toThrow('parameters must be larger than zero'); }) 
  })
)

Our second case should now cover for the execution path that leads to an exception being thrown. Let’s rerun our coverage report:

Summary

We’ve looked at how to write tests. We’ve also looked at how to debug our tests using an extension from VS Code which has allowed us to set breakpoints.

Furthermore, we’ve learned what snapshots are and how to best use them to our advantage.

Next up we’ve been looking at leveraging mocking to ensure we are in complete isolation when we test.

Lastly, we’ve looked at how we can generate coverage reports and how that can help you to track down parts of your code that could really benefit from some more testing.

Pagination in ReactJs

Pagination in ReactJs

There are a lot of resourceful materials online that give good insights into pagination in ReactJs, as well as NPM packages you can easily use

There are a lot of resourceful materials online that give good insights into pagination in ReactJs, as well as NPM packages you can easily use. As much as I appreciate those materials and love to use those packages, they mostly deal with loading the whole dataset on the page first then completely handle the pagination in the frontend. I am approaching this article with the concept of loading the exact data needed on the page, then manually loading other dataset based on the request when the user clicks the pagination number display. Below is the content structure to guide us through this article:

Table of Contents
  • Project Setup
  • HTML and CSS Styling
  • Pagination Data Format
  • Sample API request
  • Displaying the initial data
  • Showing Page Number and getting Other data
Project Setup

We are going to use create-react-app v0.1.0 which has the CSS Module configured already. Open your terminal and cd to the folder you want the project installed. Then run the below command:

npx create-react-app pagination  --use-npm

The above command will download the project into the folder calledpagination. You need to cd into the folder and run npm start. If everything goes well, you will have a page that looks like below:

HTML and CSS Styling

Open the project in your favorite code editor and locate the App.js file, We need to prepare our App.js to the look exactly like the way we want it by adding the HTML code and CSS style below:

Create a new file called App.module.css in the same directory where you have your App.js, then import it into your App.js using:

import styles from './App.module.css';

I want us to handle the display of the pagination number first, below is the style and HTML structure of what we are going to use.

  render() {
    

    return (
      <div className={styles.app}>
        
        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
              <tr>
                <td>1</td>
                <td>Abel</td>
                <td>Agoi</td>
              </tr>
              <tr>
                <td>2</td>
                <td>Muyiwa</td>
                <td>Aregbesola</td>
              </tr>
              <tr>
                <td>3</td>
                <td>Opeyemi</td>
                <td>Agoi</td>
              </tr>
              <tr>
                <td>4</td>
                <td>Ope</td>
                <td>Aina</td>
              </tr>
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span>&laquo;</span>
          <span className={styles.active}>1</span>
          <span>2</span>
          <span>3</span>
          <span>4</span>
        </div>

      </div>
    );
  }

pagination_01.js

Add the content below into your App.module.css.

.app {
    width: 50%;
    margin: 0 auto;
}

table {
  border-collapse: collapse;
  border-spacing: 0; 
}


table {
  border-collapse: separate;
  border-spacing: 0;
  color: #4a4a4d;
  font: 14px/1.4 "Helvetica Neue", Helvetica, Arial, sans-serif;
  width: 100%;
}
tr {
  overflow-x: scroll;
}
th,
td {
  padding: 15px 15px;
  vertical-align: middle;
  /* text-align: left; */
}
thead {
  font-size: 14px;
  line-height: 24px;
  font-family: Lato;
  border: 1px solid transparent;

  max-width: 100%;
  font-weight: 900;
  line-height: 24px;
  mix-blend-mode: normal;

  color: rgba(51, 51, 51, .5);
  background: rgba(255, 255, 255, .9);
}
thead tr th {
  padding: 15px 15px;
  border: 1px solid transparent;


  text-align: left;
}
tbody {
  max-width: 100%;
}
tbody tr:nth-child(odd) {
  background: #f0f0f2;
}
tbody tr:hover {
  background: #f0f0f2;
}
td {
  padding: 15px 15px;
}
td:first-child {
}


.pagination {
    margin-top: 25px;
}
.pagination span {
  cursor: pointer;
  color: black;
  float: left;
  padding: 8px 16px;
  text-decoration: none;
  transition: background-color .3s;
  border: 1px solid #ddd;
}

.pagination span.active {
  background-color: #0099FF;
  color: white;
  border: 1px solid #0099FF;
}

pagination_app.module.css

Sorry for the plenty code written so far :), I want us to have a good looking table with pagination style in place before we move into the actual paging. If everything goes well, your view should look like below:

Pagination Data Format

In most cases, when you are making API calls to an endpoint that returns a paginated data, you need to pass at least the page number with the URL, hence a sample URL will look like below:

https://reqres.in/api/users?page=2

The most important thing to take note of in the URL above is the page=2 where 2 is the page number dataset we want to get. It can be 3,4 or any number as much as the dataset we have in the backend.

The response will always contain three important data which are per_page, total and the actual data we want to loop through. A sample response looks like below:

Sample API request

Talking about making an API request to the backend, We need a backend to make the request to, I decide to use https://reqres.in/ as the API endpoint for this tutorial because it is free, always available and reliable. You can decide to make your API request directly inside your component’s ComponentDidMount() or dispatch an action to redux from your ComponentDidMount() but for the purpose of this tutorial, we are going to make the API call from the App.js componentDidMount().

Firstly, we need to set the component’s state like below inside your App.js

  state = {
    users: null,
    total: null,
    per_page: null,
    current_page: null
  }

pagination_component_state.js

users is going to be the data we are going to loop over, while total and per_page is going to help us with calculating paging logic while the current_page will be used to style the active pagination link.

The next thing we should do is create a helper method that will serve the purpose of making an HTTP request to the API endpoint and also update the state with the response data. The method will look like below:

  makeHttpRequestWithPage = async pageNumber => {
    let response = await fetch(`https://reqres.in/api/users?page=${pageNumber}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
    });

    const data = await response.json();

    this.setState({
      users: data.data,
      total: data.total,
      per_page: data.per_page,
      current_page: data.page,
    });
  }

pagination_http_request.js

This method will accept a parameter called pageNumber so it can be reusable and will always update the state with the right data when the response is successful.

Since on page load, we need to make the HTTP request to the backend, and we are going to do this inside thecomponentDidMount() by calling the method above and passing it the first-page number we want which should be 1. Hence, the componentDidMount() will look like below:

 componentDidMount() {
    this.makeHttpRequestWithPage(1);
  }

pagination_componentDidMount.js

If we add console.dir(this.state.users) inside the render() method, below will be printed in the console

The null was before the data arrived, once the data arrived, it updates the state, hence the array of users data.

Displaying the initial data

Haven gotten the data needed, we need to loop through the data and display it. Hence we can update our render method to have below:

    let users;

    if (this.state.users !== null) {
      users = this.state.users.map(user => (
        <tr key={user.id}>
          <td>{user.id}</td>
          <td>{user.first_name}</td>
          <td>{user.last_name}</td>
        </tr>
      )); 
    }
    
    return (
      <div className={styles.app}>
        
        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
              { users }
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span>&laquo;</span>
          <span className={styles.active}>1</span>
          <span>2</span>
          <span>3</span>
          <span>4</span>
          <span>&raquo;</span>
        </div>

      </div>
    );

gistfile1.txt

I replaced the dummy data we had inside the with the result of the loop which I equated to users. We have the assurance that when the state changes, ReactJs will automatically update the content of the table. The final stage is displaying the page logic and getting the other contents based on the page number clicked which will be sent to the API endpoint.

Showing Page Number and getting other data

Before we talk about showing page number automatically using the desired logic, I want us to manually show those numbers and make the actual API calls when the numbers are clicked. For now, we are going to hard code the pagination numbers ourselves like below:

<div className={styles.pagination}>
  <span onClick={() => this.makeHttpRequestWithPage(1)}>1</span>
  <span onClick={() => this.makeHttpRequestWithPage(2)}>2</span>
  <span onClick={() => this.makeHttpRequestWithPage(3)}>3</span>
  <span onClick={() => this.makeHttpRequestWithPage(4)}>4</span>
</div>

pagination_hard_code.js

The above code will look like below when previewed in the browser.

Notice that each span has an event handler attached to it, and I passed the page number to that event handler, so anytime we click on the pagination link, it will make a new HTTP request and update the component states, hence the user’s table data. We do not want to hard-code the links as we did above, so we need to automatically display those links.

So we’re planning on showing the page numbers for a series of pieces of data so that users can easily navigate multiple items. There are a few things that we need to know first:

  • The page that we’re on
  • Total number of items
  • Number of items per page

Good news is that we have captured all these things in our component’s state.

Next, we need to look at how we want to display the page numbers, there is a wide range of methods that people use:

  • Simple Next/Previous buttons with no numbers
  • A list of all possible pages
  • Page 1 & the last page, with the current page (and 2 above/below) shown

I personally prefer to show the very first page, that last page, and then the current page with 2 pages above & below. So for example on page 12 out of 24 pages we’d see:

1, 10, 11, 12, 13, 14, 24

This allows users to quickly navigate to the start, and to the end, as well as jump through multiple pages at once. For the purpose of this tutorial, I am going to show us how to show a list of all possible pages(item two above) then item three too.

The Arithmetic

We need to work out the total number of pages, for this, we want to take the total number of items that there are, and divide it by the number of items per page. But we want to make sure that we take that number and round it up.

So if there were 12 items in total, and we were showing 5 per page, we’d have a total of 3 pages of items. If we were to show 3 per page, we’d show 4 pages.

const pageNumbers = [];
for (let i = 1; i <= Math.ceil(this.state.meta.total / this.state.meta.per_page); i++) {
    pageNumbers.push(i);
}

page_logic_pagination.js

Haven gotten the page numbers, we need to loop through to display the span since we want to show all possible numbers first, our loop will look like below:

renderPageNumbers = pageNumbers.map(number => {
  let classes = this.state.current_page === number ? styles.active : '';

  return (
    <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
  );
});

pagination_all_numbers_loop.js

We need to update our pagination view to look like below:

<div className={styles.pagination}>
  <span onClick={() => this.makeHttpRequestWithPage(1)}>&laquo;</span>
  {renderPageNumbers}
</div>

pagination_view._01js

Congrats, we have successfully handle pagination, make HTTP request to the backend and changing the table content when user click on the page number to see.

To be sure we are on the same page, my App.js code looks like below:

import React, { Component } from 'react';
import styles from './App.module.css';

class App extends Component {


  state = {
    users: null,
    total: null,
    per_page: null,
    current_page: 1
  }


  componentDidMount() {
    this.makeHttpRequestWithPage(1);
  }


  makeHttpRequestWithPage = async pageNumber => {
    const response = await fetch(`https://reqres.in/api/users?page=${pageNumber}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
    });

    const data = await response.json();

    this.setState({
      users: data.data,
      total: data.total,
      per_page: data.per_page,
      current_page: data.page
    });
  }


  render() {

    let users, renderPageNumbers;

    if (this.state.users !== null) {
      users = this.state.users.map(user => (
        <tr key={user.id}>
          <td>{user.id}</td>
          <td>{user.first_name}</td>
          <td>{user.last_name}</td>
        </tr>
      ));
    }

    const pageNumbers = [];
    if (this.state.total !== null) {
      for (let i = 1; i <= Math.ceil(this.state.total / this.state.per_page); i++) {
        pageNumbers.push(i);
      }


      renderPageNumbers = pageNumbers.map(number => {
        let classes = this.state.current_page === number ? styles.active : '';

        return (
          <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
        );
      });
    }

    return (


      <div className={styles.app}>

        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
            {users}
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span onClick={() => this.makeHttpRequestWithPage(1)}>&laquo;</span>
          {renderPageNumbers}
          <span onClick={() => this.makeHttpRequestWithPage(1)}>&raquo;</span>
        </div>

      </div>
    );
  }

}

export default App;

pagination_app.js

and my view like below:

We can change the page number display logic to below since it will accommodate for large dataset.

renderPageNumbers = pageNumbers.map(number => {
  let classes = this.state.current_page === number ? styles.active : '';

  if (number == 1 || number == this.state.total || (number >= this.state.current_page - 2 && number <= this.state.current_page + 2)) {
    return (
      <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
    );
  }
});

pagination_another_display_logic.js

Thanks for reading.

URLs and Webpack in Reactjs

URLs and Webpack in Reactjs

URLs and Webpack in Reactjs - I am still a newbie in ReactJS. I've been following series of tutorials and articles on the framework and decided to start putting what I've learnt so far into practice.

My website, dillionmegida.com was built with PHP. You could check it out as I highly appreciate reviews. I'm though aspiring to be a full-stack javascript developer so I'm in some way trying to depart from PHP :)

I decided to replicate my homepage using React and to broaden my skills in using components.

It was going quite successful until I tried using an <img> JSX element. I used it like;

import React from 'react'
 
let dpStyle = {
    // Some styles
}
 
let Profilepic = () => (
    <div className={dpStyle}>
        <img src='../img/deee.jpeg' alt='My profile picture'/>
    </div>
)
export default Profilepic;

The img folder was a sub-directory of the src folder.

My aim here was to have my profile picture as a component with some styling to be used on my homepage and any other desired page. The src for the img tag was not been used appropriately as my image was not displayed.

I paused to think of the problem, inspected my page and discovered the src displayed there was exactly as I inputted it. So silly of me :( I made some researches which helped me remember that most attributes of JSX element are not as mostly used with HTML, but have to be enclosed in curly braces.

import React from 'react'
 
let dpStyle = {
    // Some styles
}
 
let Profilepic = () => (
    <div className={dpStyle}>
        <img src={'../img/deee.jpeg'} alt='My profile picture'/>
    </div>
)
export default Profilepic;

I tried rendering the page again and my image was still not displayed.

Funny enough, I quickly thought of a trick (for the first time);

...
import Dp from '../img/deee.jpeg'
...
let Profilepic = () => (
    <div className={dpStyle}>
        <img src={Dp} alt='My profile picture'/>
    </div>
)

To my amazement, it worked. I was excited and at the same time sad, with feeling of little guilt. I didn't know why. lol. I said to myself, 'I am not doing the right thing :(' and also asked myself, 'Is react as crazy as this?'

I headed back to google to make some more research and got to discover that the webpack that React (create-react-app) automatically installed had been configured to use the public folder (at the same level with src folder) for relative URLs (such as my image URL).

Using ...<img src={'../img/deee.jpeg'} />..., React was actually checking the public folder for the image sub-directory which it couldn't find.

Solution

1. Change the location of the image folder

I changed the location of the image folder making it a sub-directory under the public directory and it worked as expected.

2. Use the require keyword

Just as the import keyword is used for relative URLs, the require keyword does same. So, I was able to do this;

import React from 'react'
 
let dpStyle = {
    // Some styles
}
 
let Profilepic = () => (
    <div className={dpStyle}>
        <img src={require('../img/deee.jpeg')} alt='My profile picture'/>
    </div>
)
export default Profilepic;

and in the other pages where the Profilepic component was required and used, my image displayed perfectly. :D

Like I said, I am still a newbie at ReactJS but I hope this little experience would be helpful for you too. There are other super powers of webpacks too which I would still be learning. Please do share in the comment section corrections on this article and also helpful Tips.

I'll be documenting my experience in my ReactJS journey as much as I can. Stay tuned and thanks for reading.

Originally published by Megida Dillion at dillion.hashnode.dev

=======================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Understanding TypeScript

☞ Typescript Masterclass & FREE E-Book

☞ React - The Complete Guide (incl Hooks, React Router, Redux)

☞ Modern React with Redux [2019 Update]

☞ The Complete React Developer Course (w/ Hooks and Redux)

☞ React JS Web Development - The Essentials Bootcamp

☞ React JS, Angular & Vue JS - Quickstart & Comparison

☞ The Complete React Js & Redux Course - Build Modern Web Apps

☞ React JS and Redux Bootcamp - Master React Web Development