0

My app consists of the following: It's an app using the MERN stack and the goal is to be a able for users to login, register and view contacts. (It's just for practice, I have no plans on deploying it).

The problem occurs when the User logs in, and then goes to the contacts, I can console.log them, getting them from Express in the back end, but I can't display them. On top of that, when I console.log the length, it says 0, even though when I console.log the array it shows the items, and it's something I cannot comprehend.

Here's the App.js

// Importing React
import React from 'react';

// Importing the Router
import { BrowserRouter as Router, Switch, Route} from "react-router-dom";

// Get Login Functional Component
import Register from "./Register";

// Get the Register FC
import LogIn from "./LogIn";

// Get the Contacts FC
import Contacts from "./Contacts";

// Get the USER Context
import {UserContextProvider} from "./Context/UserContext";


function App() {
  return (
    <UserContextProvider>
        <Router>
          <div className="App">
            <Switch>
              <Route exact path="/register" component={Register} />
              <Route exact path="/login" component={LogIn} />
              <Route exact path="/contacts" component={Contacts} />
            </Switch>
          </div>
        </Router>
      </UserContextProvider>
  );
}

export default App;

As you can see I'm using the React Router, and here are, firstly the Register.js

import React, {useState} from 'react';
import axios from "axios";
import {Link} from "react-router-dom";

function Register() {

  // Getting Email Input with UseState
  const [email, setEmail] = useState("");

  // Getting Password Input with UseState
  const [password, setPassword] = useState("");

  // Getting Confirmation Password Input with UseState
  const [confirmationPassword, setConfirmationPassword] = useState("");

  // If passwords are matching
  const [passwordsMatch, setPasswordsMatch] = useState("");

  // To Clear Input Fields
  const clearFields = () => {setEmail(""); setPassword("");}

  // Function that changes the email state when the user types
  const changeEmail = (e) => {
    setEmail(e.target.value);
  }
  // Function that changes the password state when the user types
  const changePassword = (e) => {
    setPassword(e.target.value);
  }

  // Function that changes the confirmation password state when the user types
  const changeConfirmationPassword = (e) => {
    setConfirmationPassword(e.target.value);
  }

  // When the user submits the form
  const registerSubmit = (e) => {
    
    // So it doesn't go to another page
    e.preventDefault();

    if (password === confirmationPassword) {
      setPasswordsMatch(true);

      // Passing data to backend
      axios.post("http://localhost:5000/post", {email, password})
      .then(response => console.log(response))
      .catch(error => console.log(error.data));

      // Clear all the fields
      clearFields();
    } else {
      setPasswordsMatch(false);
    }
  }

  return (
    <div>
      <div className="container mt-4">
        <form onSubmit={(e) => registerSubmit(e)}>
          <div className="form-group">
            <label htmlFor="exampleInputEmail1">Email address</label>
            <input type="email" className="form-control" onChange={(e) => changeEmail(e)} id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={email}></input>
            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
          </div>
          <div className="form-group">
            <label htmlFor="exampleInputPassword1">Password</label>
            <input onChange={(e) => changePassword(e)} type="password" className="form-control" id="exampleInputPassword1" placeholder="Password" value={password}></input>
          </div>
          <div className="form-group">
            <label htmlFor="exampleInputPassword2">Confirm Password</label>
            <input onChange={(e) => changeConfirmationPassword(e)} type="password" className="form-control" id="exampleInputPassword12" placeholder="Password" value={confirmationPassword}></input>
            {/* If Passwords Match then Show Alert */}
            {passwordsMatch === false && 
              <div className="alert alert-danger mt-4" role="alert">
                Passwords don't match!
              </div>
            }
          </div>
          <button type="submit" className="btn btn-primary">Register</button>
        </form>
        <h5 className="mt-4 mb-4">If you already have an account</h5>
        <Link to={"/login"} className="btn btn-secondary">I already have an account</Link>
      </div>
    </div>
  )
}

export default Register;

Here's the LogIn.js:

import React, {useState, useContext, history} from 'react';
import {Link} from "react-router-dom";
import axios from "axios";

// Get User Context
import {UserContext} from "./Context/UserContext";

function LogIn() {
  // Getting the ID from the context
  const {id, setId} = useContext(UserContext);

  // Getting Email Input with UseState
  const [email, setEmail] = useState("");

  // Getting Password Input with UseState
  const [password, setPassword] = useState("");

  const [logIn, setLogIn] = useState(undefined);

  // Function that changes the email state when the user types
  const changeEmail = (e) => {
    setEmail(e.target.value);
  }
  // Function that changes the password state when the user types
  const changePassword = (e) => {
    setPassword(e.target.value);
  }

  // When the user submits the form
  const onLoginSubmit = (e) => {

    // So it doesn't go to another page
    e.preventDefault();

    // Check Credentials
    axios.get("http://localhost:5000")
    .then(response => { for (const index in response.data) {
      const person = response.data[index];
        if (person.email === email && person.password === password) {
          // User has succesfully logged in
          setLogIn(true);
          // Store Secret ID in variable
          const secretID = person._id;
          // Change Context
          setId(secretID);
          // Break the loop
          break;
        } else {
          setLogIn(false);
        }
      };
    })
    .catch(error => console.log(error.data));
  }

  return (
    <div>
      <div className="container mt-4">
        <form onSubmit={(e) => onLoginSubmit(e)}>
          <div className="form-group">
            <label htmlFor="exampleInputEmail1">Email address</label>
            <input type="email" className="form-control" onChange={(e) => changeEmail(e)} id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={email}></input>
            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
          </div>
          <div className="form-group">
            <label htmlFor="exampleInputPassword1">Password</label>
            <input onChange={(e) => changePassword(e)} type="password" className="form-control" id="exampleInputPassword1" placeholder="Password" value={password}></input>
          </div>
          <button type="submit" className="btn btn-primary">Log In</button>
        </form>
        {logIn === false && 
          <div className="alert alert-danger mt-4" role="alert">
            Wrong credentials!
          </div>
        }
        {logIn === true &&
          <div className="alert alert-success mt-4" role="alert">
            Correct information!
          </div>
        }
        <h5 className="mt-4 mb-4">If you don't have an account</h5>
        <Link to={"/register"} className="btn btn-secondary">I don't have an account</Link>
        {logIn && <Link to={"/contacts"} className="btn btn-secondary ml-4">Contacts</Link>}
      </div>
    </div>
  )
}

export default LogIn;

Here you can see that I'm using the Context Hook, here's the file where I create it (UserContext.js):

import React, {createContext, useState} from 'react'

export const UserContext = createContext();

export function UserContextProvider(props) {

  // Create the ID variable
  const [id, setId] = useState("");

  return (
    <UserContext.Provider value={{id, setId}}>
      {props.children}
    </UserContext.Provider>
  )
}

And lastly, here's my Contact.js:

import React, {useContext, useState} from 'react';
import {UserContext} from "./Context/UserContext";
import axios from "axios";

// To display single contacts
import SingleContact from "./SingleContact";

function Contacts() {
  const {id, setId} = useContext(UserContext);

  const [email, setEmail] = useState("");

  const contacts = [];


  // Get User's Info from database
     // Check Credentials
     axios.get("http://localhost:5000/contacts")
     .then(response => { for (const index in response.data) {
       const contact = response.data[index];
         if (contact.id === id) {
           contacts.push(contact);
         }
       };
     })
     .catch(error => console.log(error.data));


  return (
    <div>
      <h1>Contacts</h1>
      {console.log(contacts)}
      {console.log(contacts.length)}
      {contacts.forEach(contact => <h1>{contact}</h1>)}
    </div>
  )
}

export default Contacts

Here the forEach loop isn't working, and I have no idea why, because when I print the array of Contacts, I get them completely.

Here's what I get in the console:

Results of console.logging the contacts array and its length

I'm not providing the files from the backend because, as you can see, the problem is clearly on the front-end, because I'm getting the data, but not displaying it correctly.

gxp2
  • 149
  • 2
  • 11
  • 2
    try `{contacts.map(contact =>

    {contact}

    )}` in your return function, the important part being "map" instead of "ForEach". Map returns a new array, which would be JSX. ForEach does not return anything.
    – GBourke Aug 03 '20 at 16:23
  • You [should not use `for..in` over an array](https://stackoverflow.com/q/500504/215552), if that's what `response.data` is... Also note that `contacts` is being populated after the `contacts.forEach()` is running, since `axios.get` is async. – Heretic Monkey Aug 03 '20 at 16:28
  • Does this answer your question? [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Heretic Monkey Aug 03 '20 at 16:28
  • I second @Gandzal 's remark about using `map`, it's much cleaner and will avoid a few problems. With the array length of zero I suspect that you may be running into a problem with some sort of asynchronous behavior. What happens if you console log the length of the contacts list in the callback to your axios.get? – kingsfoil Aug 03 '20 at 16:32

1 Answers1

0

You might just want to try .map function of array.

The usual patterns -

return (
    <div>
      <h1>Contacts</h1>
      {contacts.map((contact,index) => <h1 key={index}>{JSON.stringify(contact)}</h1>)}
    </div>
  )

Just as @Gandzal mentioned, .forEach will loop every value inside the array but it won't return anything whereas .map will return an array.

In your case, .map will return array of JSX elements.

Note: You can replace JSON.stringifying the whole object with contact.id or contact.emailContact as you wish.

Kevin Moe Myint Myat
  • 1,916
  • 1
  • 10
  • 19