Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialOsaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsWhy is my Scoreboard app not displaying?
I followed Guil meticulously through each and every step of this course. Only for me to get this error in my chrome browser console as the Scoreboard didn't display.
warning.js:33 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.
invariant.js:42 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. at invariant (invariant.js:42) at ReactCompositeComponentWrapper.instantiateReactComponent [as _instantiateReactComponent] (instantiateReactComponent.js:72) at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:364) at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:255) at Object.mountComponent (ReactReconciler.js:43) at mountComponentIntoNode (ReactMount.js:102) at ReactReconcileTransaction.perform (Transaction.js:141) at batchedMountComponentIntoNode (ReactMount.js:124) at ReactDefaultBatchingStrategyTransaction.perform (Transaction.js:141) at Object.batchedUpdates (ReactDefaultBatchingStrategy.js:60
I went back to look through the videos to compare his code and mine if there's any difference, I found few but the app still didn't display in the browser.
index.js
import React from 'react';
import { render } from 'react-dom';
import Provider from 'react-redux';
import { createStore } from 'redux';
import PlayerReducer from './src/reducers/player';
import Scoreboard from './src/container/Scoreboard';
const store = createStore(
PlayerReducer,
window.devToolsExtension && window.devToolsExtension()
);
render(
<Provider store={store}>
<Scoreboard />
</Provider>,
document.getElementById('root')
);
Scoreboard.js
// Container component
import React, { PropTypes, Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as PlayerActionCreators from '../actions/player'
import AddPlayerForm from '../components/AddPlayerForm';
import Player from '../components/Player';
import Header from '../components/Header';
// Container component
class Scoreboard extends Component {
static propTypes = {
players: PropTypes.array.isRequired
}
render() {
const { dispatch, players } = this.props;
const addPlayer = bindActionCreators(PlayerActionCreators.addPlayer, dispatch);
const removePlayer = bindActionCreators(PlayerActionCreators.removePlayer, dispatch);
const changePlayerScore = bindActionCreators(PlayerActionCreators.changePlayerScore, dispatch);
const playerComponents = players.map((player, index) => {
<Player
index={index}
name={player.name}
score={player.score}
key={player.name}
changePlayerScore={changePlayerScore}
removePlayer={removePlayer}
/>
});
return (
<div className="scoreboard" >
<Header players={players} />
<div className="players">
{playerComponents}
</div>
<AddPlayerForm addPlayer={addPlayer} />
</div>
)
}
}
const mapStateToProps = (state) => {
{
players: state.players
}
}
export default connect(mapStateToProps)(Scoreboard);
player.js...\actiontypes
// Actions expressed as string constants
import React from 'react';
export const ADD_PLAYER = 'player/ADD_PLAYER';
export const REMOVE_PLAYER = 'player/REMOVE_PLAYER';
export const CHANGE_PLAYER_SCORE = 'player/CHANGE_PLAYER_SCORE';
player.js...\reducers
// Reducers
import * as PlayerActionTypes from '../actiontypes/player';
const initialState = [
{
name: 'Jim Hoskins',
score: 31,
},
{
name: 'Andrew Chalkley',
score: 20,
},
{
name: 'Alena Holligan',
score: 50,
}
];
export default function Player(state=initialState, action) {
switch (action.type) {
case PlayerActionTypes.ADD_PLAYER:
return [
...state,
{
name: action.name,
score: 0
}
];
case PlayerActionTypes.REMOVE_PLAYER:
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
];
case PlayerActionTypes.CHANGE_PLAYER_SCORE:
return state.map((player, index) => {
if (index === action.index) {
return {
...player,
score: player.score + action.score
};
}
return player;
});
default:
return state;
}
}
player.js...\actions
// Action creator
import * as PlayerActionTypes from '../actiontypes/player';
export const addPlayer = (name) => {
return {
type: PlayerActionTypes.ADD_PLAYER,
name
}
}
export const removePlayer = (index) => {
return {
type: PlayerActionTypes.REMOVE_PLAYER,
index
}
}
export const changePlayerScore = (index, score) => {
return {
type: PlayerActionTypes.CHANGE_PLAYER_SCORE,
index,
score
}
}
AddPlayerForm.js
// Logical component
import React, { Component, PropTypes } from 'react';
export default class AddPlayerForm extends Component {
static propTypes = {
addPlayer: PropTypes.func.isRequired,
};
state = {
name: ''
};
onNameChange = (e) => {
const name = e.target.value;
this.setState({ name: name });
}
addPlayer = (e) => {
if (e) e.preventDefault();
this.props.addPlayer(this.state.name);
this.setState({ name: '' });
}
render = () => {
return (
<div className="add-player-form">
<form onSubmit={this.addPlayer}>
<input
type="text"
value={this.state.name}
onChange={this.onNameChange}
placeholder="Player Name"
/>
<input type="submit" value="Add Player" />
</form>
</div>
);
}
}
Player.js
// Pure component with component dependency
import React, { PropTypes } from 'react';
import Counter from './Counter';
const Player = (props) => {
return (
<div className="player">
<div className="player-name">
<a className="remove-player" onClick={() => props.removePlayer(props.index)}>✖</a>
{props.name}
</div>
<div className="player-score">
<Counter
index={props.index}
changePlayerScore={props.changePlayerScore}
score={props.score} />
</div>
</div>
)
}
Player.propTypes = {
name: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
score: PropTypes.number.isRequired,
removePlayer: PropTypes.func.isRequired,
changePlayerScore: PropTypes.func.isRequired
};
export default Player;
Counter.js
// Pure component
import React, { PropTypes } from 'react';
const Counter = (props) => {
return (
<div className="counter" >
<button className="counter-action decrement" onClick={() => props.changePlayerScore(props.index, -1)}>
-
</button>
<div className="counter-score"> {props.score} </div>
<button className="counter-action increment" onClick={() => props.changePlayerScore(props.index, 1)}>
+
</button>
</div>
);
}
Counter.propTypes = {
changePlayerScore: PropTypes.func.isRequired,
index: PropTypes.number.isRequirerd,
score: PropTypes.number.isRequired,
};
export default Counter;
Header.js
// Pure component with component dependency
import React, { PropTypes } from 'react';
import Stats from './Stats';
import Stopwatch from './Stopwatch';
const Header = (props) => {
return (
<div className="header">
<Stats players={props.players} />
<h1>Scoreboard</h1>
<Stopwatch />
</div>
);
}
Header.propTypes = {
players: PropTypes.array.isRequired,
};
export default Header;
Stopwatch.js
// Logical component
import React, { Component, PropTypes } from 'react';
export default class Stopwatch extends Component {
state = {
running: false,
previouseTime: 0,
elapsedTime: 0
};
componentDidMount() {
this.interval = setInterval(this.onTick);
}
componentWillUnmount() {
clearInterval(this.interval);
}
onStart = () =>
this.setState({
running: true,
previousTime: Date.now(),
});
onStop = () =>
this.setState({
running: false,
});
onReset = () =>
this.setState({
elapsedTime: 0,
previousTime: Date.now(),
});
onTick = () => {
if (this.state.running) {
var now = Date.now();
this.setState({
elapsedTime: this.state.elapsedTime + (now - this.state.previousTime),
previousTime: Date.now(),
});
}
}
render = () => {
var seconds = Math.floor(this.state.elapsedTime / 1000);
return (
<div className="stopwatch" >
<h2>Stopwatch</h2>
<div className="stopwatch-time"> {seconds} </div>
{this.state.running ?
<button onClick={this.onStop}>Stop</button>
:
<button onClick={this.onStart}>Start</button>
}
<button onClick={this.onReset}>Reset</button>
</div>
)
}
}
Stats.js
// Pure component
import React, { PropTypes } from 'react';
const Stats = (props) => {
const playerCount = props.players.length;
const totalPoints = props.players.reduce(function (total, player) {
return total + player.score;
}, 0);
return (
<table className="stats">
<tbody>
<tr>
<td>Players:</td>
<td>{playerCount}</td>
</tr>
<tr>
<td>Total Points:</td>
<td>{totalPoints}</td>
</tr>
</tbody>
</table>
)
}
Stats.propTypes = {
players: PropTypes.array.isRequired
}
export default Stats;
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsDone :)
1 Answer
Calin Bogdan
14,921 PointsMy guess is that Guil recorded the tutorial on React 15 at most and your version of React is v16.
You can find this on the React website:
React.PropTypes has moved into a different package since React v15.5. Please use the prop-types library instead.
Later edit after I figured out I forgot to explain: You can see at the top of each file that you're importing React and PropTypes from the react package.
import React, { Component, PropTypes } from 'react';
After you run npm install --save prop-types you need to change that into:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
Hope this helped!
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsHow can I use the prop-types library?
Calin Bogdan
14,921 PointsRun
npm install --save prop-types
in the console / terminal inside the project folder
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsThank you very much Calin for all the assistance but unfortunately nothing changed. I'm still getting the same error in the browser console
Calin Bogdan
14,921 PointsIn index.js you're importing Scoreboard component from the container folder
import Scoreboard from './src/container/Scoreboard';
Any chance you might have mistyped containers?
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsJust cross-checked, no misspelling errors with the container directory.
Calin Bogdan
14,921 PointsI found a missing semicolon in Scoreboard.js
import * as PlayerActionCreators from '../actions/player' <---
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsLol, I didn't even notice that. You have an Eagle's eye Calin. The Scoreboard is still not displaying. I really don't know what's wrong with this code TBH.
Calin Bogdan
14,921 PointsIn index.js,
import Provider from 'react-redux';
should be
import { Provider } from 'react-redux';
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsNice one Calin, we're getting somewhere. The error in the browser console has changed. These are the new errors:
mapStateToProps() in Connect(Scoreboard) must return a plain object. Instead received undefined. warning @ bundle.js:30824
bundle.js:10836 Warning: Failed prop type: The prop players
is marked as required in Scoreboard
, but its value is undefined
.
in Scoreboard (created by Connect(Scoreboard))
in Connect(Scoreboard)
in Provider
bundle.js:33242 Uncaught TypeError: Cannot read property 'map' of undefined at Scoreboard.render (bundle.js:33242) at bundle.js:19683 at measureLifeCyclePerf (bundle.js:18963) at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (bundle.js:19682) at ReactCompositeComponentWrapper._renderValidatedComponent (bundle.js:19709) at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19249) at ReactCompositeComponentWrapper.mountComponent (bundle.js:19145) at Object.mountComponent (bundle.js:17486) at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19258) at ReactCompositeComponentWrapper.mountComponent (bundle.js:19145)
Calin Bogdan
14,921 PointsThe current implementation of mapStateToProps is equivalent to:
function mapStateToProps(state) {
{
players: state.players
}
}
// current implementation
const mapStateToProps = (state) => {
{
players: state.players
}
}
It doesn't return anything. If you check again, Guil used parentheses, not curly braces. This way, the arrow function knows that it has to return an object.
const mapStateToProps = (state) => ( <----
{
players: state.players
}
) <----
// the above is the same with:
function mapStateToProps(state) {
return {
players: state.players
}
}
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsCalin, I might not know you but I know you have the right if not best attitude for a developer-an undying spirit to solve a problem/challenge. The error in the browser console has changed, the first error is gone. Leaving only the remaining two:
bundle.js:10836 Warning: Failed prop type: The prop players
is marked as required in Scoreboard
, but its value is undefined
.
in Scoreboard (created by Connect(Scoreboard))
in Connect(Scoreboard)
in Provider
ncaught TypeError: Cannot read property 'map' of undefined at Scoreboard.render (bundle.js:33242) at bundle.js:19683 at measureLifeCyclePerf (bundle.js:18963) at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (bundle.js:19682) at ReactCompositeComponentWrapper._renderValidatedComponent (bundle.js:19709) at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19249) at ReactCompositeComponentWrapper.mountComponent (bundle.js:19145) at Object.mountComponent (bundle.js:17486) at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19258) at ReactCompositeComponentWrapper.mountComponent (bundle.js:19145)
From the error message I noticed that these errors have the same root problem i.e. the 'players' prop. Fixing it should eliminate both of them, killing two birds with a stone.
Calin Bogdan
14,921 PointsIt's undefined because the initialState is an array with no name. Therefore the players array couldn't be found. You should define the initialState in the player reducer as an object with an array named players inside:
// the current initialState:
const initialState = [
{
name: 'Jim Hoskins',
score: 31,
},
{
name: 'Andrew Chalkley',
score: 20,
},
{
name: 'Alena Holligan',
score: 50,
}
];
// how it should be
const initialState = {
players: [
{
name: 'Jim Hoskins',
score: 31,
},
{
name: 'Andrew Chalkley',
score: 20,
},
{
name: 'Alena Holligan',
score: 50,
}
]
};
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsBrilliant Calin just brilliant! Finally the Scoreboard is being displayed but not fully. The list of players isn't displayed but every other thing is and there are no errors in the console. The stopwatch is working fine-start, stop and restart buttons. But once I try to add a player (to the empty list of players in the Scoreboard), those previous two errors pop up in the console and the player isn't added.
Calin Bogdan
14,921 PointsSame story:
const playerComponents = players.map((player, index) => {
<Player
index={index}
name={player.name}
score={player.score}
key={player.name}
changePlayerScore={changePlayerScore}
removePlayer={removePlayer}
/>
});
either return the <Player> component or change the outside curly braces into parentheses:
const playerComponents = players.map((player, index) => ( <----
<Player
index={index}
name={player.name}
score={player.score}
key={player.name}
changePlayerScore={changePlayerScore}
removePlayer={removePlayer}
/>
)); <-----
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsGood one, now the user interface of the whole Scoreboard app is being displayed. But in the console there's an error:
warning.js:33 Warning: Failed prop type: Counter: prop type index
is invalid; it must be a function, usually from React.PropTypes.
in Counter (created by Player)
in Player (created by Scoreboard)
in div (created by Scoreboard)
in div (created by Scoreboard)
in Scoreboard (created by Connect(Scoreboard))
in Connect(Scoreboard)
in Provider
And both the increment and decrement buttons aren't functional, whenever I press any of them this error pops up in the console:
player.js:44 Uncaught TypeError: state.map is not a function at Player (player.js:44) at computeNextEntry (<anonymous>:2:27469) at recomputeStates (<anonymous>:2:27769) at <anonymous>:2:31382 at Object.dispatch (createStore.js:178) at dispatch (<anonymous>:2:31875) at Object.changePlayerScore (bindActionCreators.js:7) at onClick (Counter.js:13) at HTMLUnknownElement.boundFunc (ReactErrorUtils.js:63)
Then when I try to add a new player, the usual two errors still pop up.
Calin Bogdan
14,921 PointsI know that what I'm going to say might sound lazy, but there are a lot of errors and typos you missed when working along with Guil. Try doing the redux course again to be sure you understand everything. I'm helping with making the Scoreboard work, but our goal is to make you understand how it works and how you can fix potential errors.
Osaro Igbinovia
Full Stack JavaScript Techdegree Student 15,928 PointsYou're right Calin, I didn't even realize I made so much syntax errors. Thank you very much for all the effort, you really spurred me to never give up no matter how messed up ones code is. You're a living legend bro, keep it up.
Calin Bogdan
14,921 PointsCalin Bogdan
14,921 PointsHello!
Could you please show us the code you're currently struggling with? It's easier to find the error this way.
Thanks!