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.
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. Commented Apr 22, 2019 at 0:28
  • Possible duplicate of Cannot read property 'map' of undefined Commented Apr 22, 2019 at 0:29
  • @AlexanderStaroselsky You're confusing state and props. persons comes from App.jsx props, not the Persons.jsx state. Commented Apr 22, 2019 at 0: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. Commented Apr 22, 2019 at 0:36
  • @AlexanderStaroselsky Sure, it can be avoided that way. Or, we can get simpler and just see it for what it is: a typo. Commented Apr 22, 2019 at 0:38

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.