Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invariant Violation: Objects are not valid as a React child when firing a function

Tags:

reactjs

Having an issue firing this function if this.state.number is equal to the numbers in the array.

  delay = async () => {
     const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
     await sleep(1000);
     return <Redirect to="/dashboard" />
  }

error

Invariant Violation: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

Home.js

import React, {Component} from 'react';
import {Redirect} from "react-router-dom";
import Grid from '@material-ui/core/Grid';
import './App.css';
import { withRouter } from 'react-router-dom'

class Home extends Component {

    state = {
        number: "",

        numbers: ["111-111-1112, 111-111-1111, 111-111-1116, 111-111-1118"]
      };


      onChange = e => {
        e.preventDefault();

        this.setState({
          number: e.target.value
        });
      };

      delay = async () => {
        const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
        await sleep(1000);
        return <Redirect to="/dashboard" />
      }

      validate = myNum => this.state.numbers.find(num => num === myNum);
      render(){

        const validatedNumber = (
          <div>
            <span style={{ color: "green" }}>Activated ✅ </span>
            {this.delay()}
          </div>
        );
        return (
          <div className="main">
             <Grid container >
               <Grid item sm={8} md={12} >
                <input
                type="text"
                placeholder="Validate Number"
                value={this.state.number}
                onChange={this.onChange}
                />
            <h2>
              {this.validate(this.state.number) ? (
                validatedNumber
              ) : (
                <div>Not in the system</div>
              )}
            </h2>

               </Grid>   

             </Grid>

          </div>
        );

      }
}

export default Home;
like image 931
BARNOWL Avatar asked Mar 04 '23 19:03

BARNOWL


2 Answers

You are getting this error beucase this.delay() will return a Promise object and you can't render objects.

Any function that have async returns a Promise.

To solve this, you need a if to decide what to return and a state to keep the status of the setTimeout.

  1. Keep a state of shouldRedirect

    state = {
       ...
       shouldRedirect: false
    }
    
  2. Update the state when the setTimeout finishes

    delay = async () => {
        const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
        await sleep(1000);
        this.setState({shouldRedirect: true})
    }
    
  3. Check in the render method if shouldRedirect

    render(){
        if(this.state.shouldRedirect){
            return <Redirect to="/dashboard" />
        }
        // continue with normal render
        ...
    }
    
  4. Call delay in the correct place

    onChange = e => {
        e.preventDefault();
        let number = e.target.value
        this.setState({number});
        if(this.state.numbers.find(num => num === number)){
            this.delay()
        }
    };
    
like image 191
Vencovsky Avatar answered Apr 09 '23 19:04

Vencovsky


You have to capture the value of the event before passing it to setState or else the event might go out of scope before state is set.

import React, {Component} from 'react';
import {Redirect} from "react-router-dom";
import Grid from '@material-ui/core/Grid';
import './App.css';
import { withRouter } from 'react-router-dom'

class Home extends Component {
    state = {
        number: "",
        numbers: ["111-111-1112, 111-111-1111, 111-111-1116, 111-111-1118"],
        isValid: false
      };

      onChange = e => {
        const number = e.target.value
        this.setState({ number });
        if (this.isValid(number)) {
          setTimeout(() => this.props.history.push('/dashboard'), 1000)
          this.setState({ isValid: true })
        }
      };

      isValid = myNum => this.state.numbers.find(num => num === myNum);

       render() {
        const { number, isValid } = this.state
        return (
          <div className="main">
             <Grid container >
               <Grid item sm={8} md={12} >
                <input
                  type="text"
                  placeholder="Validate Number"
                  value={number}
                  onChange={this.onChange}
                />
                <h2>
                 {isValid ? (
                  <div>
                    <span style={{ color: "green" }}>Activated ✅ </span>
                  </div>
                 ) 
                 : (
                   <div>Not in the system</div>
                )}
              </h2>
            </Grid>   
          </Grid>
        </div>
       );
     }
}
export default withRouter(Home);
like image 42
Avin Kavish Avatar answered Apr 09 '23 20:04

Avin Kavish