0

I'm getting this compile error, but I'm unclear about what's undefined. This all looks like it should work, or at least not produce errors, but maybe I'm missing something obvious? Any assistance would be much appreciated. I'm happy to upload any additional code that might shed some light on the problem.

←→1 of 2 errors on the page
TypeError: Cannot read property 'map' of undefined
Persons.render
src/Components/Persons.jsx:14
  11 | }  
  12 | 
  13 |   render() {
> 14 |     let personsList = this.props.persons.map( (  persons, index) => {
     | ^  15 |       return <div className="personsListLinks"><li key={index}><Link to={`/api/persons/${persons.id}`}>{persons.name}</Link></li></div>
  16 |     })
  17 |     return (
View compiled

This is the Component:

import { BrowserRouter as Link } from "react-router-dom";

class Persons extends Component {
  constructor(props){
    super(props)
    this.state = {
      persons: '',
      personsList: ''
    }
  }  

    render() {
      let personsList = this.props.persons.map( (  persons, index) => {
        return <div className="personsListLinks"><li key={index}><Link to={`/api/persons/${persons.id}`}>{persons.name}</Link></li></div>
      })
      return (
        <div className="PersonsList">
        <h1>Persons</h1>
        {personsList}
      </div>
    )
  }
}
export default Persons;

And, App.jsx...

import './App.css';
import Signup from './Signup';
import Login from './Login';
import UserProfile from './UserProfile';
import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom";
import axios from 'axios';
import PersonsShow from './Components/PersonsShow';
import Persons from './Components/Persons';
import { set } from 'mongoose';

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      token: '',
      user: null,
      errorMessage: '',
      lockedResult: '',
      persons: [],
      personality: [],
      quotes: []
    }
    this.liftTokenToState = this.liftTokenToState.bind(this)
    this.checkForLocalToken = this.checkForLocalToken.bind(this)
    this.logout = this.logout.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.addNewPerson = this.addNewPerson.bind(this)
    // this.addQuote = this.addQuote.bind(this)

  }

  checkForLocalToken() {
    // Look in localStorage for the token
    let token = localStorage.getItem('mernToken')
    if (!token || token === 'undefined') {
      // There is no token
      localStorage.removeItem('mernToken')
      this.setState({
        token: '',
        user: null
      })
    } else {
      // Found a token, send it to be verified
      axios.post('/auth/me/from/token', {token})
      .then( res => {
        if (res.data.type === 'error') {
          localStorage.removeItem('mernToken')
          this.setState({errorMessage: res.data.message})
        } else {
          // Put token in localStorage
          localStorage.setItem('mernToken', res.data.token)
          // Put token in state
          this.setState({
            token: res.data.token,
            user: res.data.user
          })
        }
      })
    }
  }
  componentDidMount() {
    this.checkForLocalToken()
    this.getPersons()
  }

  getPersons = () => {
    axios.get('/persons')
    .then(res => {
        this.setState({
            persons: res.data
        })
    })
  }


  liftTokenToState({token, user}) {
    this.setState({
      token,
      user
    })
  }

  logout(){
    // Remove the token from localStorage
    localStorage.removeItem ('mernToken')
    // Remove the user and token from state
    this.setState({
      token: '',
      user: null
    })
  }

  handleClick(e) {
    e.preventDefault()
    // axios.defaults.headers.common['Authorization'] = `Bearer ${this.state.token}`
    let config = {
      headers: {
        Authorization: `Bearer ${this.state.token}`
      }
    }
    axios.get('/locked/test', config).then( res => {
      // console.log("this is the locked response:" , res);
      this.setState({
        lockedResult: res.data
      })
    })
  }

  handleNewPersonClick(person) {
    this.setState({
      person: person
    })
  }
  addNewPerson(e) {
    e.persist()
    e.preventDefault()
    let user = this.props.user
    let person = this.props.persons
    axios.post(`/api/user/:id/person${user._id}/`, {
      name: e.target.name
    }).then(res => {
      axios.get(`/api/user/${user._id}/persons`).then(res => {
        this.setState({persons: res.data})
      })
    })
  }

  render() {
    let user = this.state.user
    let contents;
    if (user) {
      contents = (
        <Router>
        <Route exact path='/' render= {() => <Redirect to='/persons' /> } />
        <Route exact path='/' render={() => <Persons persons={this.state.persons} user={this.state.user} logout={this.logout} />}/>
        {/* <Route path="/persons/:pid" render={(props) => <PersonsShow person={this.state.persons} addItem={this.addItem} user={user} logout={this.logout} {...props} />}/> */}
        {/* <Route path="/cart" render={() => <CartPage cart={this.state.cart} />}/> */}
        <UserProfile user={user} logout={this.logout} />
        <Persons person={this.state.persons} addItem={this.addItem} user={user} logout={this.logout}/>
        {/* <PersonsShow user={user} /> */}
          {/* <Persons user={user} /> */}
          {/* <p><a onClick={this.handleClick}>Test the protected route...</a></p> */}
          <p>{this.state.lockedResult}</p>
        </Router>
      )
    } else {
      contents = (
      <>
        <Signup liftToken={this.liftTokenToState} />
        <Login liftToken={this.liftTokenToState} />
      </>
      )
    }
    return (
      <div className="App">
        <header><h1>Welcome to Judge-O-Matic!</h1></header>
        <div className="content-box">
        {contents}
        </div>
      </div>
    )
  }
}

export default App;

I've tried rewriting as a functional component with similar results.
mickeychcg
  • 43
  • 7
  • 1
    You need to set persons to default of empty array or use conditional rendering. map() does not exist on string type which is current default. This is asked every day multiple times a day. – Alexander Staroselsky Apr 22 '19 at 00:28
  • Possible duplicate of [Cannot read property 'map' of undefined](https://stackoverflow.com/questions/24706267/cannot-read-property-map-of-undefined) – Alexander Staroselsky Apr 22 '19 at 00:29
  • @AlexanderStaroselsky You're confusing `state` and `props`. `persons` comes from `App.jsx` `props`, not the `Persons.jsx` `state`. – AryanJ-NYC Apr 22 '19 at 00:29
  • @AryanJ-NYC Sure I overlooked that but this can 100% be solved by default props and/or conditional rendering given this is an async request and they are attempting to render before anything has resolved. – Alexander Staroselsky Apr 22 '19 at 00:36
  • @AlexanderStaroselsky Sure, it can be avoided that way. Or, we can get simpler and just see it for what it is: a typo. – AryanJ-NYC Apr 22 '19 at 00:38
  • @AryanJ-NYC Makes sense to me. You got an upvote from me :) – Alexander Staroselsky Apr 22 '19 at 00:39
  • @AlexanderStaroselsky, thanks - yes I missed that, map()'s not going to work there.I'll switch it to conditional rendering. – mickeychcg Apr 22 '19 at 00:43

1 Answers1

3

You have a typo:

<Persons person={this.state.persons} addItem={this.addItem} user={user} logout={this.logout}/>

should be

<Persons persons={this.state.persons} addItem={this.addItem} user={user} logout={this.logout}/>

Please note the s at the end of the first prop.

AryanJ-NYC
  • 2,529
  • 2
  • 20
  • 27