React App在localhost上完美运行,但在Heroku上出错

时间:2017-09-14 05:40:42

标签: ruby-on-rails reactjs heroku redux

今天非常令人沮丧的错误。我花了整整一天的时间来尝试调试我的本地主机上完美运行的小应用程序,但偶尔出现在heroku上的错误。

如果我多次刷新页面,我可以实现登录。但它需要2-3次刷新。

登录用户时遇到的两个错误是 -

Uncaught TypeError: Cannot read property 'exercise_name' of undefined

并且

Uncaught TypeError: Cannot read property '_currentElement' of null

现在我基本上知道问题所在。当我最初尝试映射它们时,我绝不能拥有我的道具。其中一个道具是一系列练习,其中一个键是“练习名称”。'我猜这与我从本地主机收到的速度有关,与heroku的ajax调用相比。

这是我的问题,

我不知道它来自哪个组件,因为我在4个组件中使用了exercise_name。 Heroku有行号,但它们没有任何帮助,因为它没有指向我的应用程序中的任何内容,我不能像我在我的机器上那样删除heroku中的调试器。

我已尝试在mapStateToProps中设置默认道具,如此 -

allExercises: state.entities.exercise || []

没用。

我尝试在我的组件中包装条件。哈斯没有工作。

以下四个组件使用exercise_name。任何方向都将不胜感激。

我理解以下是很多代码。我完全满足于一个答案,让我知道如何找到哪些代码行在heroku上产生这些错误,或者如何在heroku上调试一般。

组件1

import React from 'react';
import { withRouter } from 'react-router-dom';
import SetResultContainer from '../setresult/create_setresult_container';

class ExerciseIndex extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      inputVal: '',
      active: 'FIRST',
      name: ''
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e) {
    this.setState({ inputVal: e.target.value })
  }

  componentDidMount() {
    this.props.requestAllExercises();
  }

  handleClick(e) {
    this.setState({ inputVal: e.currentTarget.attributes.value.value})
  }

  handleSubmit(e) {
    let newActive = this.state.active === 'FIRST' ? 'SECOND' : null
    let allExercises = this.props.allExercises;
    let selected;
    let name;
    if (allExercises) {
      allExercises.forEach(exercise => {
        if (exercise.exercise_name === this.state.inputVal) {
          selected = exercise,
          name = exercise.exercise_name
        }
      })
      e.preventDefault();
    }
    if (!name) {
      this.setState({inputVal: 'Invalid Input, Please try Again'})
      return 'Invalid Input'
    }
    this.props.requestExercise(selected)
    this.setState({inputVal: '', active: newActive, name: name})
    this.props.requestAllExercises();
  }


  render() {


    let allExercises = this.props.allExercises || [{ exercise_name: '' }]

      let match = allExercises.map((exercise) => {
        if (this.state.inputVal === '') return [];
        let matched = [];
        if (this.state.inputVal.length > 0) {
          for (var j = 0; j < this.state.inputVal.length; j++) {
            matched = [];
            if (exercise.exercise_name.slice(0, j + 1).toUpperCase() === this.state.inputVal.slice(0, j + 1).toUpperCase()) {
              matched.push(<li onClick={this.handleClick}
                               value={exercise.exercise_name}
                               className="workout-auto-li"
                               key={exercise.id}>{exercise.exercise_name}</li>);
            }
          }
        } else {
            matched.push(<li onClick={this.handleClick}
                             value={exercise.exercise_name}
                             className="workout-auto-li"
                             key={exercise.id}>{exercise.exercise_name}</li>)
        }
        return matched;
      });


    return (
      <div>
        {this.props.allExercises ? (
          <div>
            {this.state.active === 'FIRST' ? (
              <div className="exercise-main-div">
                <div className="exercise-second-div">
                  <label className="exercise-label">
                    <h3>Add an Exercise for {this.props.liftname}</h3>
                    <input type="text" value={this.state.inputVal}
                      onChange={this.handleChange}
                      className="exercise-input"
                      />
                  </label>
                  <ul className="exercise-ul">
                    {match}
                  </ul>
                  <button className="new-exercise-button"
                    onClick={this.handleSubmit}>Add Exercise</button>
                </div>
              </div>
            ) : this.state.active === 'SECOND' ? (
              <SetResultContainer user={this.props.user}
                exercises={this.props.exercises}
                exercise={this.state.name}
                liftname={this.props.liftname}/>
            ) : null }
          </div>
        ) : null }
      </div>
    );
  }
}

export default withRouter(ExerciseIndex);

组件2

import React from 'react';
import { withRouter } from 'react-router';
import values from 'lodash/values'
import { Pie } from 'react-chartjs-2';

class Leaderboard extends React.Component {

  constructor(props) {
    super(props)
    this.state = { exercise: null }
    this.handleUpdate = this.handleUpdate.bind(this)
  }

  componentDidMount() {
    this.props.requestAllUsers();
    this.props.requestAllExercises();
  }

  handleUpdate(property) {
    return e => this.setState({ [property]: e.target.value });
  }

  render() {
    const members = this.props.members.map(member => {
      return <li className="members-list" key={member.id + 1}>{member.username}</li>
    })

    const memberId = {}
    this.props.members.map(member => {
      memberId[member.username] = member
    })

    const membersSetResults = {}
    const membersLiftMaxes = {}
    const completedMemberExercises = []
    const completedExercises = {}

    this.props.members.map(member => {

      if (member.workouts) {
        let workouts = values(member.workouts)
        for (var i = 0; i < workouts.length; i++) {
          let workoutResult = workouts[i].setresults
          let results = values(workoutResult)
          if (membersSetResults[member.username]) {
            membersSetResults[member.username].unshift(results)
          } else {
            membersSetResults[member.username] = [results];
          }
        }
      }
    })

    Object.keys(membersSetResults).map(member => {
      let setResults = membersSetResults[member]
      membersLiftMaxes[member] = {}
      for (var i = 0; i < setResults.length; i++) {
        let sets = setResults[i]
        for (var j = 0; j < sets.length; j++) {
          let currentExercise = this.props.allExercises[sets[j].exercise_id]
          let exercise = currentExercise.exercise_name

          if (completedMemberExercises.indexOf(exercise) < 0 && currentExercise.ex_type === 'lift') {
            completedMemberExercises.push(exercise)
          }

          if (completedExercises[exercise]) {
            completedExercises[exercise] += 1
          } else if (!completedExercises[exercise]) {
            completedExercises[exercise] = 1
          }

          if (currentExercise.ex_type === 'lift') {
            if (membersLiftMaxes[member][exercise]) {
              if(membersLiftMaxes[member][exercise] < sets[j].weight_lifted) {
                membersLiftMaxes[member][exercise] = sets[j].weight_lifted
              }
            } else if (!membersLiftMaxes[member][exercise]) {
                membersLiftMaxes[member][exercise] = sets[j].weight_lifted
            }
          }
        }
       }
     })


       const PieChart = {
         datasets: [{
           data: Object.values(completedExercises),
           backgroundColor: [
             '#2D4262',
             '#363237',
             '#73605B',
             '#D09683',
             '#F1F3CE',
             '#1E656D',
             '#00293C',
             '#F0810F',
             '#75B1A9',
           ],
         }],

         labels: Object.keys(completedExercises)
       };



     let exerciseDropdown = completedMemberExercises.map((exercise, idx) => {
       return <option key={idx} value={exercise}>{exercise}</option>
     })

     let sorted = [];
     const memberAndMax = {}
     Object.keys(membersLiftMaxes).map(member => {
       if (this.state.exercise) {
         let exerciseMax = membersLiftMaxes[member][this.state.exercise]

         if(!memberAndMax[this.state.exercise]){
           memberAndMax[this.state.exercise] = []
           memberAndMax[this.state.exercise].push([member, exerciseMax])
         } else if (memberAndMax[this.state.exercise]) {
           memberAndMax[this.state.exercise].push([member, exerciseMax])
         }

         memberAndMax[this.state.exercise].map(max => {
           if (sorted.indexOf(max) < 0) {
             if (max[1] > 0) {
               sorted.push(max)
             }
           }
         })

         sorted.sort((a, b) => {
           return a[1] - b[1]
         })
       }
     })


     let maxLis = sorted.reverse().map((user) => {

       if (memberId[user[0]].id === this.props.cu.id) {
         return <li className='userPresent' key={memberId[user[0]].id}>
                            <p className="members-list-p">{user[0]}</p>
                            <p className="members-list-p-two">{user[1]}</p></li>
       } else {
         return <li className='members-list' key={memberId[user[0]].id}>
                            <p className="members-list-p">{user[0]}</p>
                            <p className="members-list-p-two">{user[1]}</p></li>
       }

     })




    return (

      <div className='main-leaderboard'>
        <div className='lb-reset-div'>
          <button className='lb-reset-button' onClick={() => this.setState({exercise: null})}>Reset</button>
          <select className='leaderboard-dropdown' onChange={this.handleUpdate('exercise')}>
            <option>Please Select</option>
            {exerciseDropdown}
          </select>
        </div>
        {(this.state.exercise) ? (
          <div className='lb-ul-div'>
            <h3 className='selected-ex-title'>{this.state.exercise}</h3>
            <ul className='leaderboard-ul'>
              <li className="members-list"><p className="members-list-p">Name</p>
              <p className="members-list-p-two">Max (lbs)</p></li>
              {maxLis}
            </ul>
          </div>
        ): (!this.state.exercise) ? (
          <div className='lb-ul-div'>
            <h3 className='selected-ex-title'>Leaderboard</h3>
            <ul className='leaderboard-ul'>
              {members}
            </ul>
          </div>
        ): null}
          <div className='pie-chart-div-lb'>
            <h3 className='pie-chart-header'>What the World's Doing</h3>
            <Pie circumfrence={300} data={PieChart}/>
          </div>
      </div>
    )
  }
}


export default withRouter(Leaderboard); 

组件3

import React from 'react';
import { withRouter } from 'react-router';
import values from 'lodash/values';
import { Line, Pie } from 'react-chartjs-2';

class SearchBestWorkouts extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      inputVal: '',
      name: '',
      active: '',
      result: ''
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  componentDidMount() {
    this.props.requestAllExercises();
    this.setState({active: 'FIRST'})
  }


  handleChange(e) {
    e.preventDefault(e)
    this.setState({ inputVal: e.target.value })
  }

  handleClick(e) {
    this.setState({ inputVal: e.currentTarget.attributes.value.value})
  }

  handleSubmit(e) {
    let newActive = this.state.active === 'FIRST' ? 'SECOND' : 'FIRST'
    let allExercises = values(this.props.exercises);

    let selected;
    let name;
    if (newActive === 'SECOND') {
      allExercises.forEach(exercise => {
        if (exercise.exercise_name === this.state.inputVal) {
          selected = exercise,
          name = exercise.exercise_name
        }
      })
      e.preventDefault();
      if (!name) {
        this.setState({inputVal: 'Invalid Input, Please try Again'})
        return 'Invalid Input'
      }

      this.setState({inputVal: '', active: newActive, name: name})
      this.props.requestAllExercises();
    } else if (newActive === 'FIRST') {
      this.setState({inputVal: '', active: newActive, name: '' })
    }

  }

  render () {

    let allWorkouts = this.props.allWorkouts;
    let exercises = this.props.exercises;

    let setResults = allWorkouts.map(workout => {
      return values(workout.setresults)
    })

    let mergedSets = [].concat.apply([], setResults)

    const allResults = {}
    const exerciseTypes = {}
    const completedExercises = {};

    for (var i = 0; i < mergedSets.length; i++) {
      let set = mergedSets[i];
      let exercise = exercises[set.exercise_id]
      let name = exercise.exercise_name
      let bodypart = exercise.bodypart

      if (exerciseTypes[bodypart]) {
        exerciseTypes[bodypart] += 1
      } else if (!exerciseTypes[bodypart]) {
        exerciseTypes[bodypart] = 1
      }

      if (exercise.ex_type === 'lift') {
        if (!allResults[name]) {
          allResults[name] = { labels: [],
                               datasets: [{
                                 label: 'Weight over Time',
                                 backgroundColor: '#2988BC',
                                 borderColor: '#2F496E',
                                 data: [],
                              }],
                            };
        }


        if (completedExercises[name] < (set.weight_lifted)) {
          completedExercises[name] = set.weight_lifted
        } else if (!completedExercises[name]) {
          completedExercises[name] = set.weight_lifted
        }

        allResults[name].labels.push(allResults[name].labels.length + 1)
        allResults[name].datasets[0].data.unshift(set.weight_lifted)
      }


    }


    const PieChart = {
      datasets: [{
        data: Object.values(exerciseTypes),
        backgroundColor: [
          '#2D4262', '#363237', '#73605B', '#D09683'
        ],
      }],

      labels: Object.keys(exerciseTypes)
    };




    const best = Object.keys(completedExercises).map((exercise) => {
      if (this.state.inputVal === '') return [];
      let bests = [];
      if (this.state.inputVal.length > 0) {
        for (var j = 0; j < this.state.inputVal.length; j++) {
          bests = [];
          if (exercise.slice(0, j + 1).toUpperCase() === this.state.inputVal.slice(0, j + 1).toUpperCase()) {
            bests.push(<li onClick={this.handleClick}
                             value={exercise}
                             className="best-lift-li"
                             key={exercise.id}>{exercise}</li>);
          }
        }
      } else {
        bests.push(<li onClick={this.handleClick}
                         value={exercise}
                         className="best-lift-li"
                         key={exercise.id}>{exercise}</li>)
      }
      return bests;
    });

    return (
     <div>
       {this.state.active === 'FIRST' ? (
         <div className="best-lift-div">
           <div className='best-lift-div-two'>
             <h3 className="best-lift-title">Personal Records</h3>
             <div className='best-lift-input-div'>
               <input type="text" value={this.state.inputVal}
                 onChange={this.handleChange}
                 className="best-lift"
                 placeholder="Enter an Exercise"
                 />
             </div>
             <ul className='best-lift-ul'>
               {best}
             </ul>
             <button className='best-lift-button' onClick={this.handleSubmit}>Best Lift</button>
           </div>


         </div>
       ) : this.state.active === 'SECOND' ? (
         <div className="best-lift-div">
           <div className='best-lift-div-two'>
             <h3 className="best-lift-title">
               {this.state.name}: {completedExercises[this.state.name]}</h3>
             <div className='chart-background'>
               <Line width={250} height={200} data={allResults[this.state.name]}/>
             </div>
             <button className='best-lift-button' onClick={this.handleSubmit}>Back</button>
           </div>
           <div className='best-lift-div-three'>
             <h3 className="best-lift-title">Workout Analysis</h3>
             <div className='pie-chart-background'>
               <Pie circumfrence={100} data={PieChart} />
             </div>
           </div>
         </div>
       ) : null}
     </div>
    )
  }
}


export default withRouter(SearchBestWorkouts)

组件4

import React from 'react';
import values from 'lodash/values'
import InfiniteScroll from 'react-infinite-scroll-component';
import { withRouter } from 'react-router'

class WorkoutShow extends React.Component {
  constructor(props) {
    super(props);

    this.handleClick = this.handleClick.bind(this);
  }

  handleClick (e) {
    e.preventDefault();
    this.props.deleteWorkout(this.props.selectedWorkout).then(
      () => {
        this.props.requestUser(this.props.match.params.userId)
      }
    )
    this.props.toggleParent();
  }

  render () {
    const setArray = values(this.props.selectedWorkout.setresults)
    const exercises = this.props.exercises
    const results = setArray.map((result, idx) => {
      if (result.workout_id === this.props.selectedWorkout.id) {
        return <li key={result.id} className='workout-show-li'>
                   <p className='workout-title'><p>Set {idx + 1}: </p><p>{exercises[result.exercise_id].exercise_name}</p></p>
                   <ul>
                     {result.weight_lifted ? (
                       <li className='workout-result-li'><p className='workout-result-li'>Weight:</p>{result.weight_lifted}{result.weight_unit}</li>
                     ) : null}
                     {result.reps ? (
                       <li className='workout-result-li'><p className='workout-result-li'>Reps:</p>{result.reps}</li>
                     ) : null}
                     {result.distance ? (
                       <li className='workout-result-li'><p className='workout-result-li'>Distance:</p>{result.distance}{result.distance_unit}</li>
                     ) : null}
                     {result.hour || result.min || result.sec ? (
                       <li className='workout-result-li'><p className='workout-result-li'>Duration:</p>
                         <div className='dur-format'>
                           {result.hour ? (
                             <p className='dur-result-hour'>{result.hour}:</p>
                           ) : null}
                           {result.min ? (
                             <p className='dur-result'>{result.min}:</p>
                           ) : null}
                           {result.sec ? (
                             <p className='dur-result'>{result.sec}</p>
                           ) : null}
                         </div>
                       </li>
                     ) : null }
                   </ul>
                  </li>
      }
    })

    return (
      <div className="workout-show-main">
        <h3 className="workout-show-title">{this.props.selectedWorkout.name}
          <button className='remove-workout-button' onClick={this.handleClick}>DELETE</button></h3>
          <InfiniteScroll>
            <ul className="workout-show-ul">
             {results}
            </ul>
         </InfiniteScroll>
      </div>
    );
  }

}

export default withRouter(WorkoutShow);

1 个答案:

答案 0 :(得分:2)

在服务器上引入数据获取的延迟,以及那些事情  完全在本地工作也不能在服务器上工作。

主要罪魁祸首是您的代码,它会对返回值进行假设。例如

const setArray = values(this.props.selectedWorkout.setresults)
const exercises = this.props.exercises
const results = setArray.map((result, idx) => {

该块的第3行盲目地假定setArray被定义并且是一个数组。您需要在每一步添加检查,或提供默认值(请参阅下面第一行的结尾)

const setArray = values(this.props.selectedWorkout.setresults) || []
const exercises = this.props.exercises
const results = setArray.map((result, idx) => {

我将此作为练习留给您添加我称之为“防御性代码”的练习。检查返回值并处理丢失的数据而不用barfing。

您还可以添加try..catch块来跟踪您的防御性代码无法处理的任何错误。

这需要一些时间,但是如果你想编写好的代码,那么值得升级你的方法以将其作为标准做法包含