反应错误'TypeError:无法将undefined或null转换为object'

时间:2017-09-11 17:24:00

标签: javascript node.js reactjs express

我不知道为什么我在控制台中收到错误。

TypeError: Cannot convert undefined or null to object
    at ContestList (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/src/components/ContestList.jsx:8:15)
    at /Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactCompositeComponent.js:305:16
    at measureLifeCyclePerf (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactCompositeComponent.js:75:12)
    at ReactCompositeComponentWrapper._constructComponentWithoutOwner (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactCompositeComponent.js:304:14)
    at ReactCompositeComponentWrapper._constructComponent (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactCompositeComponent.js:279:21)
    at ReactCompositeComponentWrapper.mountComponent (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactCompositeComponent.js:187:21)
    at Object.mountComponent (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactReconciler.js:45:35)
    at ReactDOMComponent.mountChildren (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactMultiChild.js:236:44)
    at ReactDOMComponent._createContentMarkup (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactDOMComponent.js:659:32)
    at ReactDOMComponent.mountComponent (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/node_modules/react-dom/lib/ReactDOMComponent.js:526:29)
TypeError: Cannot read property 'initialMarkup' of undefined
    at /Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/my-fullstack-javascript-app/routes/index.js:12:47
    at process._tickDomainCallback (internal/process/next_tick.js:135:7)

这是有问题的文件:

import React from 'react';
import ContestPreview from './ContestPreview';
import PropTypes from 'prop-types';

export default function ContestList({ contests, onContestClick }) {
  return (
    <div className="ContestList">
      {Object.keys(contests).map(contestId => (
        <ContestPreview
          key={contestId}
          onClick={onContestClick}
          {...contests[contestId]}
        />
      ))}
    </div>
  );
}

ContestList.propTypes = {
  contests: React.PropTypes.object,
  onContestClick: React.PropTypes.func.isRequired
};

这是我的App.jsx文件:

import React, { Component } from 'react';
import Header from './Header';
import ContestList from './ContestList';
import Contest from './Contest';
import * as api from '../api';

function pushState(obj, url) {
  return window.history.pushState(obj, '', url);
}

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = this.props.initialData;
    this.fetchContest = this.fetchContest.bind(this);
    this.propTypes = {
      initialData: React.PropTypes.object.isRequired
    };
  }

  componentDidMount() {
    // This is the life cycle method that guarantees that the DOM has been mounted in the browser successfully.
    console.log('did Mount'); // ajax call, timers, listeners
  }

  componentWillUnmount() {
    //This is the life cycle method that says the component is about to be unmounted.
    console.log('will unmount'); //clear timers, listeners
  }

  fetchContest(contestId) {
    pushState({ currentContestId: contestId }, `/contest/${contestId}`);
    api.fetchContest(contestId).then(contest => {
      this.setState({
        currentContestId: contest.id,
        contests: {
          ...this.state.contests,
          [contest.id]: contest
        }
      });
    });
  }

  pageHeader() {
    if (this.state.currentContestId) {
      return this.currentContest().contestName;
    }

    return 'Naming Contests';
  }

  currentContest() {
    return this.state.contests[this.state.currentContestId];
  }

  currentContent() {
    if (this.state.currentContestId) {
      return <Contest {...this.currentContest()} />;
    }
    return (
      <ContestList
        onContestClick={this.fetchContest}
        contests={this.state.contests}
      />
    );
  }

  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="twelve columns">
            <Header message={this.pageHeader()} />
            {this.currentContent()}
          </div>
        </div>
      </div>
    );
  }
}

这是serverRender.js文件:

//fetch the data from the api
import React from 'react';
import ReactDomServer from 'react-dom/server';

import App from './src/components/App';

var axios = require('axios');
import config from './config';

const getApiUrl = contestId => {
  if (contestId) {
    return `http://localhost:8016/api/contests/${contestId}`;
  }
  return `http://localhost:8016/api/contests`;
};

const getInitialData = (contestId, apiData) => {
  if (contestId) {
    return {
      currentContestId: apiData.id,
      contests: {
        [apiData.id]: apiData
      }
    };
  }
  return {
    contests: apiData.contests
  };
};

const serverRender = contestId =>
  axios
    .get(getApiUrl(contestId))
    .then(resp => {
      const initialData = getInitialData(contestId, resp.data);
      return {
        initialMarkup: ReactDomServer.renderToString(
          <App initialData={resp.data} />
        ),
        initialData: resp.data
      };
    })
    .catch(console.error);

export default serverRender;

这是我的路线/ api文件:

'use strict';

var express = require('express');

const router = express.Router();

import serverRender from '../serverRender';

router.get(['/', '/contest/:contestId'], function(req, res) {
  console.log(req.params.contestId);
  serverRender(req.params.contestId)
    .then(({ initialMarkup, initialData }) => {
      res.render('index', {
        initialMarkup,
        initialData
      });
    })
    .catch(console.error);
});

module.exports = router;

API / contests.js:

'use strict';

var express = require('express');
var data = require('../../src/testData');
const router = express.Router();

const contests = data.contests.reduce(function(obj, contest) {
  obj[contest.id] = contest;
  return obj;
}, {});

router.get('/', function(req, res) {
  res.send({
    contests: contests
  });
});

router.get('/:contestId', function(req, res) {
  let contest = contests[req.params.contestId];
  contest.description =
    'Food truck gluten-free banksy, fap occupy bespoke whatever mustache.  Occupy kogi kale chips chillwave, odd future typewriter iphone twee truffaut viral ethical artisan put a bird on it single-origin coffee banh mi.  Master cleanse brunch occupy trust fund marfa yr.  Chillwave ennui fap, wes anderson cliche cosby sweater brooklyn vegan organic.  Shoreditch PBR semiotics, chillwave art party photo booth terry richardson.  Synth ennui semiotics mustache pickled, biodiesel food truck cosby sweater readymade mixtape letterpress pour-over leggings.  Food truck freegan vinyl thundercats, post-ironic ennui wes anderson banh mi four loko synth photo booth authentic 3 wolf moon.';
  res.send(contest);
});

module.exports = router;

0 个答案:

没有答案