Category: React.JS

Using ReactQuery with React instead of Axios + hooks

In your React app you can use ReactQuery and ReactQueries for request caching, background updates and stale data out of the box with zero-configuration, instead of just Axios + hooks.

UseQuery : 1 api request

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
...
const getServices = () => {
        return axios.get('https://jsonplaceholder.typicode.com/posts');
};
...
const { data, isError, isLoading } = useQuery(['services'], getServices);

if (isLoading) {
    return <p>Loading...</p>;
}

if (isError) {
    console.error('error');
}

return <ServiceComponent data={data} />;

UseQueries : 2+ api request

Index.tsx

import { useQueries } from '@tanstack/react-query';
import axios from 'axios';
...
const getServiceOne = () => {
        return axios.get('https://jsonplaceholder.typicode.com/posts');
};
...
const results: any = useQueries({
    queries: [
      { queryKey: ['serviceOne'], queryFn: getServiceOne, staleTime: Infinity },
      { queryKey: ['serviceTwo'], queryFn: getServiceTwo, staleTime: Infinity },
    ],
});

const serviceOneResult = results[0];
const serviceTwoResult = results[1];

  const isError = serviceOneResult.error || serviceTwoResult.error;
  const isLoading = serviceOneResult.isLoading || serviceTwoResult.isLoading;

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (isError) {
    console.error('error');
  }

  return (
    <ServiceComponent
      dataOne={serviceOneResult.data}
      dataTwo={serviceTwoResult.data}
    />
  );

AWS Amplify : Babel error building React.JS app

Is it possible you may have an error with babel on AWS Amplify, while deploying your React.JS app:

If you click on Frontend you should be able to see the logs :

2022-01-14T17:18:36.441Z [INFO]: $ react-scripts build
2022-01-14T17:18:37.352Z [INFO]: Creating an optimized production build...
2022-01-14T17:18:38.431Z [INFO]: Failed to compile.
2022-01-14T17:18:38.433Z [INFO]: ./src/index.js
                                 Error: [BABEL] /codebuild/output/src483533555/src/app/src/index.js: Cannot find module '@babel/helper-define-map'
                                 Require stack:
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/plugin-transform-classes/lib/transformClass.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/plugin-transform-classes/lib/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/preset-env/lib/available-plugins.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/preset-env/lib/plugins-compat-data.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/preset-env/lib/normalize-options.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/node_modules/@babel/preset-env/lib/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/create.js
                                 - /codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/react-scripts/node_modules/@babel/core/lib/config/files/plugins.js
                                 - /codebuild/output/src483533555/src/app/node_modules/react-scripts/node_modules/@babel/core/lib/config/files/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/react-scripts/node_modules/@babel/core/lib/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/react-scripts/node_modules/babel-loader/lib/index.js
                                 - /codebuild/output/src483533555/src/app/node_modules/loader-runner/lib/loadLoader.js
                                 - /codebuild/output/src483533555/src/app/node_modules/loader-runner/lib/LoaderRunner.js
                                 - /codebuild/output/src483533555/src/app/node_modules/webpack/lib/NormalModule.js
                                 - /codebuild/output/src483533555/src/app/node_modules/webpack/lib/NormalModuleFactory.js
                                 - /codebuild/output/src483533555/src/app/node_modules/webpack/lib/Compiler.js
                                 - /codebuild/output/src483533555/src/app/node_modules/webpack/lib/webpack.js
                                 - /codebuild/output/src483533555/src/app/node_modules/react-scripts/scripts/build.js (While processing: "/codebuild/output/src483533555/src/app/node_modules/babel-preset-react-app/index.js")
2022-01-14T17:18:38.451Z [WARNING]: error Command failed with exit code 1.
2022-01-14T17:18:38.451Z [INFO]: info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
2022-01-14T17:18:38.461Z [ERROR]: !!! Build failed
2022-01-14T17:18:38.461Z [ERROR]: !!! Non-Zero Exit Code detected
2022-01-14T17:18:38.461Z [INFO]: # Starting environment caching...
2022-01-14T17:18:38.461Z [INFO]: # Uploading environment cache artifact...
2022-01-14T17:18:38.534Z [INFO]: # Environment caching completed
Terminating logging...

In order to solve the @babel errors, you will need to install all the @babel packages:

NPM

npm install babel-core babel-loader babel-preset-env 
babel-preset-react babel-polyfill babel-runtime @babel/helpers 
@babel/helper-builder-react-jsx-experimental @babel/helper-builder-react-jsx 
@babel/helper-define-map @babel/helper-plugin-utils @babel/helper-regex

YARN

yarn add babel-core babel-loader babel-preset-env 
babel-preset-react babel-polyfill babel-runtime @babel/helpers 
@babel/helper-builder-react-jsx-experimental @babel/helper-builder-react-jsx 
@babel/helper-define-map @babel/helper-plugin-utils @babel/helper-regex

In order to run your React.JS project on Amplify, here is all the babel packages I installed to solve all problems:

  • babel-core
  • babel-loader
  • babel-preset-env
  • babel-preset-react
  • babel-polyfill
  • babel-runtime
  • @babel/helpers
  • @babel/helper-builder-react-jsx
  • @babel/helper-builder-react-jsx-experimental
  • @babel/helper-define-map
  • @babel/helper-plugin-utils
  • @babel/helper-regex

After, that, you should be able to see your React.JS app online with Amplify:

Public Images for your React / Angular app on Google Cloud

We often need images in our React or Angular app, but putting them in the project is not clean.
So we can use Google Cloud with Cloud Storage for the images on your app.

There are many advantages of using Cloud Storage to expose public images :
– Real time maintenance (upload, delete, change, …)
– You don’t need any commit on your git (if you commit images on your Github / Gitlab for example)
– Clean project, only code files
– Light project repository to commit, images are heavy quickly

Create a Bucket

First, you need to create a Bucket (it’s like a big folder) if you don’t already have one.

Switch to Uniform access control

Then, to make your Bucket public, go to the Permissions tab, and change the access control to Uniform :

You should click Switch to Uniform, so you don’t have to make public each file one by one
You can see, if you upload your first image, that it’s not public yet

Add public access using by adding a Reader role for allUsers

You can then add the access of Object Reader to allUsers
Then you can see it’s Public to Internet and you are able to copy URL

You can now use you image anywhere, it’s on internet and anyone can see it with the URL, so you can use it to store images for your React / Angular app, all images will work.

React.JS • Full list of all icons

Examples of listing the icons in React.JS / React Native

Listing all the icons

List of all the icons

Searching icons name

Searching an icon name

How to do it ?

Importing all the icons

Import the CSS your icons in /public/index.html

<link rel="stylesheet" href="http://cdn.materialdesignicons.com/5.4.55/css/materialdesignicons.min.css" />

Use it in your source code

Use your icons in the code source in /src/App.js like that:

<span> 
   <i class={`mdi mdi-${icon?icon:''}`} aria-hidden="true"></i>
</span>

Loop on all icons

If you want a list of icons then you can loop on the list of icons, let’s say you have put all your icons in a file called iconlist.js

export const icons = [
"ab-testing",
"abjad-arabic",
"abjad-hebrew",
"abugida-devanagari",
"abugida-thai",
"access-point",
"access-point-check",
"access-point-minus",
"access-point-network",
"access-point-network-off",
"access-point-off",
"access-point-plus",
"access-point-remove",
"account",
"account-alert",
"account-alert-outline",
"account-arrow-left",
"account-arrow-left-outline",
"account-arrow-right",
"account-arrow-right-outline",
"account-box",
"account-box-multiple",
"account-box-multiple-outline",
"account-box-outline",
"account-cancel",
"account-cancel-outline",
"account-cash",
"account-cash-outline",
"account-check",
...
];

Then you can import the icons in you App.js and use it:

import { icons } from './icons';
...
{ icons.map((icon, index) => (
   <div style={{fontWeight: 'bold'}}> <i class={`mdi mdi-${icon}`} aria-hidden="true"></i> {icon} </div>
))}

You can then add a filter if you want to check that the icons contain part of the string name :

const [searchIcon, setSearchIcon] = useState(null);
...
{ icons.filter(icon => icon.includes(searchIcon) ).map((icon, index) => (
    <div style={{fontWeight: 'bold'}}> <i class={`mdi mdi-${icon}`} aria-hidden="true"></i> {icon} </div>
  )
)}

ReactJS • Responsive UI interface

You are making a React application and you want to have a responsive user interface that works both on laptop and mobile phone ?

A solution can be to use Material UI with several layout components such :

  • Container
  • Grid
  • Hidden

The grid looks like this:

<Grid container spacing={3}>
        <Grid item xs={12}>
          <Paper className={classes.paper}>xs=12</Paper>
        </Grid>
        <Grid item xs={6}>
          <Paper className={classes.paper}>xs=6</Paper>
        </Grid>
        <Grid item xs={6}>
          <Paper className={classes.paper}>xs=6</Paper>
        </Grid>
        <Grid item xs={3}>
          <Paper className={classes.paper}>xs=3</Paper>
        </Grid>
        <Grid item xs={3}>
          <Paper className={classes.paper}>xs=3</Paper>
        </Grid>
        <Grid item xs={3}>
          <Paper className={classes.paper}>xs=3</Paper>
        </Grid>
        <Grid item xs={3}>
          <Paper className={classes.paper}>xs=3</Paper>
        </Grid>
      </Grid>

As you can see, the full width of the page is xs=12. It means the full width size is equal to 12.


You can see so that with 2 grid items of xs=6, it will be two components.
And so 4 will be 3 components on the line and 3 will be 4 components on the line.

You can use this grid items when you want to display block of contents on the page, if you know Bootstrap, it’s the same.

You can put any content inside your <Grid>, it can be a <div> for example.

Using XS, SM, MD, LG and XL

Grid SizeDeviceWeb browser width
XSMobile phone screens 0px ► 600x
SMMobile phone and tablets600px ► 960px
MDLaptop960px ► 1280px
LGLaptop1280px ► 1920px
XLDesktop1920px ► …

Using breakpoints

Using breakpoints allow you to have different grid size for different devices, which allows you to have a grid adapted for mobile phone for exemple:

You can se the size 6 for laptop (SM=6 and higher) and size is 12 for mobile phone (XS=12)

Using Hidden

You can use the <Hidden> element from Material UI to hide specific items for XS/SM/MD/LG/XL items.

<Hidden only="lg">
    <Paper className={classes.paper}>Hidden on lg</Paper>
</Hidden>

OnClick inside OnClick with React

While doing some test after putting the event OnClick in a DIV inside another DIV with the Onclick event, I was not able to trigger the OnClick event that was inside the other.

<div id="div1" style={{background: 'red'}} onClick={() => { 
setClicked('div1');
}>
  DIV 1
  <div id="div2" style={{background: 'green'}} onClick={() => { 
  console.log('you clicked div2');
  setClicked('div2');
  }>
    DIV 2
  </div>
</div>
When we click on the DIV 2, it triggers the DIV 1 onClick event

If you click on the green div, and then, if you check the logs you see that the log is working, you triggered the event.
But, if you check your state, you will see that it’s div1, and not div2, but you clicked DIV 2.
Your click action has been propagate to the DIV 2.

How to solve the problem

In order to solve this problem, we will need a fix. So we can trigger both events.
The problem is that the click action is propagate to the the other div.
So, we need to check when you trigger the onClick action that the user clicked on the good one.
To do so, we can add the following instructions in the inner DIV:

if(event.target !== event.currentTarget) return;

Which will give:

<div id="div1" style={{background: 'red'}} onClick={(event) => { 
if(event.target !== event.currentTarget) return;
setClicked('div1');
}>
  DIV 1
  <div id="div2" style={{background: 'green'}} onClick={() => { 
  console.log('you clicked div2');
  setClicked('div2');
  }>
    DIV 2
  </div>
</div>

With buttons inside a DIV with 3 levels DIV

If you have 3 levels of DIV and you want only the action of the button of the div the be triggered then you can use the following instruction in the end of the OnClick:

<div id="div3" style={{background: 'yellow'}} onClick={(event)=>{
...
event.stopPropagation();
}}/>

stopPropagation will stop the click event and so the second OnClick event will not be triggered.

React.JS / React Native : Class VS Hooks

No matter if you are using React.JS or React Native, Javascript or Typescript, you can choose these 2 ways to manage the states.

Using Class OR using Hooks : What are the differences ?

Class

import React, { Component } from 'react'; 

[...]

class Button extends Component {
    constructor(props){
        super(props);

        this.state = {
            touched: false,
            selected: false,
        }
    }

  toggleTouched = () => {
    this.setState({
      touched: true
    });
  } 
    
    render(){ 
        return( 
            <button id="button" onMouseOver={this.toggleTouched} >
                click me!!
            </button>
        );
    } 
}

export default Button;

Using a class, you need to create a class that extends from a React Component. Like every class in every languages, you use the constructor to init the class. We use it to set the default values to our states. Moreover, we need to use “this” in that class to know that we are getting the values or the function in this class, this refer to the context.


Hooks

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

[...]

const Button = memo((props) => {
  const [touched, setTouched] = useState(false);
  const [selected, setSelected] = useState(false); 
 
  const toggleTouched = () => {
    setTouched(true);
  } 

  return( 
     <button id="button" onMouseOver={toggleTouched} >
        click me!!
     </button>
  );
}); 

export default Button;

Same component, working the same too, but we need a bit less code.
We don’t need a constructor, to set the default value to our states, we just use the parameter of useState.
We don’t need the “this” keyword too, because we don’t are in a class anymore.

Instead of using setState, we use useState.
When it starts if “use…”, we are talking about hooks :
– useState : set a state with the name, the setter, the initial value
– useEffect : refresh if specific state is changed, we can use it as “ComponentWillMount” if we don’t give a state in 2nd param
– useRef : Reference for JSX
– useCallback, …etc.

Hooks seems to be the new best way to manage our states and you should use it as it may save a lot of times and lines in your code.

Create a dynamic list from JSON • React Native / React.JS

How can we create a dynamic list of objects like Views or Texts, dynamically, in a loop in React Native or React.JS from a JSON response from a REST API ?

The objective is to create a dynamic interface with buttons for adding and removing elements like that :

First, we will use the data variable to store a JSON reponse (later you can change it with the API response with a fetch() function), it will contains 3 elements.

[
        {
            "id": "01",
            "name": "Charlie",
        },
        {
            "id": "02",
            "name": "Tango",
        },
        {
            "id": "03",
            "name": "Delta",
        }
] 

Then, we will create the json variable to store the parsed JSON from the data variable. It means, the data variable is only a text string and the json variable is a JSON object, so we can do json[0].id for example.

We can put the json variable as a state so if the JSON changes, the UI with refresh automaticaly.

Here is what it should look likes in your code, in the constructor of your class :

export default class DynamicList extends React.Component {
  constructor(props) {
    super(props);

    let data = `
    [
        {
            "id": "01",
            "name": "Charlie",
        },
        {
            "id": "02",
            "name": "Tango",
        },
        {
            "id": "03",
            "name": "Delta",
        }
    ]`;

    let json = JSON.parse(data);
    console.log('json ', json);

    this.state = {
      list: json,
    };
  } 

[...]

Now, to display a list from the state list (it will be this.state.list), we will use the map() function in render(), before the return, to create as many objects that we have elements in the list array.
In your map() function, you will have 2 parameters : data and i. You will have to put the i, the incremental variable, as a “key” in each object you will return as a new element – here, it’s <TouchableOpacity> :

render() {
    // HERE WE MAKE OUR LIST OF OBJECTS
    const soldierList = this.state.contracts.map((data, i) => {
      return (
        <TouchableOpacity
          key={i}
        >
          <View
            style={{
              flexDirection: 'row',
              justifyContent: 'flex-start',
              paddingTop: 5,
              paddingBottom: 5,
            }}
          >
              <Text style={{ fontFamily: 'Gotham-Medium', color: '#8DA8C7' }}>
                {dateShort(data.datestart)}
              </Text>
            </View>
            <View style={{ flexDirection: 'row', right: 0, position: 'absolute', bottom: 5 }}>
              <MaterialCommunityIcons
                name="cross"
                style={{ color: '#0261D2', lineHeight: 20 }}
              />
            </View>
          </View>
        </TouchableOpacity>
      );
    });

    // NOW WE CAN DISPLAY IT, IF THE LIST IS EMPTY WE GOT THE EMPTY LIST MESSAGE
    return (
      <ScrollView style={{ backgroundColor: '#f6f6f6', height: '100%', width: '100%' }}>
        {this.state.list.length < 1 && (
          <View style={{ alignItems: 'center' }}>
            <Text style={{ fontWeight: 'bold', color: '#0361d2', fontFamily: 'Gotham-Medium' }}>
              Empty list !!!
            </Text>
          </View>
        )}
        {this.state.list.length >= 1 && <View>{soldierList}</View>}
      </ScrollView>
    );
  }  

UPDATE (SOLUTION 2)

We can optimize it by testing directly the length of the array, making less code in the return :

render() {
     // HERE WE MAKE OUR LIST OF OBJECTS
     const soldierList = this.state.contracts.length ? this.state.contracts.map((data, i) => {
      return (
        <TouchableOpacity
          key={i}
        >
          <View
            style={{
              flexDirection: 'row',
              justifyContent: 'flex-start',
              paddingTop: 5,
              paddingBottom: 5,
            }}
          >
              <Text style={{ fontFamily: 'Gotham-Medium', color: '#8DA8C7' }}>
                {dateShort(data.datestart)}
              </Text>
            </View>
            <View style={{ flexDirection: 'row', right: 0, position: 'absolute', bottom: 5 }}>
              <MaterialCommunityIcons
                name="cross"
                style={{ color: '#0261D2', lineHeight: 20 }}
              />
            </View>
          </View>
        </TouchableOpacity>
      );
    }) : (
      <View style={{ alignItems: 'center' }}>
        <Text style={{ fontWeight: 'bold', color: '#0361d2', fontFamily: 'Gotham-Medium' }}>
          Empty list !!!
        </Text>
      </View> 
    );
 
    // NOW WE CAN DISPLAY IT, IF THE LIST IS EMPTY WE GOT THE EMPTY LIST MESSAGE
    return (
      <ScrollView style={{ backgroundColor: '#f6f6f6', height: '100%', width: '100%' }}>
        {soldierList}
      </ScrollView>
    ); 
 }