Whenever I try to add a class constructor it shows this error, Unexpected token, expected ";"
I'm trying to use a constructor to initialize the internal component state but keeps giving this error. I've tried rewriting the code but it didn't work.
Without the constructor everything works fine, Please what I'm I missing?
See the image of the error
I just started learning to react last week
import React, { Component } from 'react';
import './App.css';
function App() {
constructor(props) {
super(props);
this.state = {
list,
github
};
}
const list = [
{
title: 'React',
url: 'https://facebook.github.io/react/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://github.com/reactjs/redux',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
},
];
const github = [
{
id: 1,
name: 'Sanusi Hayatu',
username: 'hamicch',
url: 'https://github.com/hamicch',
repos: 24
},
{
id: 2,
name: 'Hayatu Michael',
username: 'Azeez',
url: 'https://github.com/azeez',
repos: 30
},
{
id: 3,
name: 'Ridwan Abdulahi',
username: 'ridwan',
url: 'https://github.com/ridwan',
repos: 50
}
];
return (
<div className="App">
{list.map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>:
<span>{item.author}</span>--
<span>{item.num_comments}</span>--
<span>{item.points}</span>
</div>
)}
<h2>GitHub Accounts</h2>
{github.map(user =>
<div key={user.id} class='acc'>
<div>
<strong>Username: </strong>
<a href={user.url}>{user.username}</a>
</div>
<div>
<strong>Name: </strong> {user.name}
</div>
<div>
<strong>Repos: </strong>
{user.repos}
</div>
</div>
)}
</div>
);
}
export default App;
This is the error page
You cannot have constructor in functional component, it is reserved for class components only.
The shorthand for property: a_function can only be used in an object literal or class.
You can't use it where an expression or statement is expected.
class Foo {
constructor() {
this.ran = Math.random();
}
}
const foo = new Foo();
const bar = new Foo();
console.log(foo.ran);
console.log(bar.ran);
You're writing a function component. There's no class, so you aren't constructing an object, so having a constructor doesn't make sense anyway.
Related
I'm trying out Vue and I ran into the issue of a component's "mounted" method not firing, I honestly can't see any reason why it won't work, there are no errors or warnings, I checked every single line at least 4 times now, and I just can't figure out what's wrong, I tried "console-logging" something when the method fires in a Post component and it worked, but it didn't when I tried doing the same thing in a Comment component, here is all the code you should need:
The Post component:
<template>
<div class="blog-post">
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<div class="comment-section">
<Comment
v-for="comment in comments"
v-bind:key="comment.id"
:name="comment.name"
:email="comment.email"
:body="comment.body"
/>
</div>
</div>
</template>
<script>
import axios from "axios";
import Comment from "./Comment";
export default {
name: "Post",
props: {
title: String,
body: String,
postId: Number,
},
components: {
Comment,
},
data() {
return {
comments: [
{
name: "comment name",
email: "comment email",
body: "comment body",
postId: 1,
id: 1,
},
],
};
},
methods: {
async getPostData() {
const url = `https://jsonplaceholder.typicode.com/comments`;
const response = await axios.get(url);
const data = await response.data;
this.comments = data.map((comment) => ({
name: comment.name,
email: comment.email,
body: comment.body,
postId: comment.postId,
id: comment.id,
}));
},
mounted() {
this.getPostData();
},
},
};
</script>
And the Comment component:
<template>
<div class="comment">
<h4>{{ name }}</h4>
<h5>{{ email }}</h5>
<p>{{ body }}</p>
</div>
</template>
<script>
export default {
name: "Comment",
props: {
name: String,
email: String,
body: String,
},
data() {
return {};
},
};
</script>
The comments render properly when I put that placeholder data myself into the comments array, so apparently the mount() and the getPostData() methods aren't firing (or one of them at least), considering I also tried console-logging as I've said before. I can't see what the issue here is at all and can't really google stuff like this since it's so specific. So far what I know is that, the API I'm fetching data from works, the URL is correct, the comments do display on the page, meaning it's not a problem with rendering, and as I said I've tried console-logging something in the getPostData and it didn't work, whereas in Blog component it did (which does exactly the same Post should do, except fetches Posts instead of Comments). In any case, any help would be appreciated, I hope I gave all the info you might need, if not, please ask.
Your mounted function is inside your methods object.
Move it out like this:
<template>
<div class="blog-post">
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<div class="comment-section">
<Comment
v-for="comment in comments"
v-bind:key="comment.id"
:name="comment.name"
:email="comment.email"
:body="comment.body"
/>
</div>
</div>
</template>
<script>
import axios from "axios";
import Comment from "./Comment";
export default {
name: "Post",
props: {
title: String,
body: String,
postId: Number,
},
components: {
Comment,
},
data() {
return {
comments: [
{
name: "comment name",
email: "comment email",
body: "comment body",
postId: 1,
id: 1,
},
],
};
},
methods: {
async getPostData() {
const url = `https://jsonplaceholder.typicode.com/comments`;
const response = await axios.get(url);
const data = await response.data;
this.comments = data.map((comment) => ({
name: comment.name,
email: comment.email,
body: comment.body,
postId: comment.postId,
id: comment.id,
}));
},
},
mounted() {
this.getPostData();
},
};
</script>
Started learning React and JS not too long ago.
I have a parent class 'App' that gets a an array of objects 'data' from data.js. App.js is sending that 'data' property down to the 'BookList' class. I am trying to map over said property in the BookList class and save elements in 'mappedBooks' but keep getting this error:
TypeError: this.props.books.map is not a function
DATA.JS:
const data = [
{
id: 1,
title: `The Pragmatic Programmer`,
author: `David Thomas, Andrew Hunt`,
img: `https://images-na.ssl-images-amazon.com/images/I/51cUVaBWZzL._SX380_BO1,204,203,200_.jpg`
},
{
id: 2,
title: `HTML and CSS: Design and Build Websites`,
author: `Jon Duckett`,
img: `https://images-na.ssl-images-amazon.com/images/I/31aX81I6vnL._SX351_BO1,204,203,200_.jpg`
},
{
id: 3,
title: `Coding All-in-one For Dummies`,
author: `Nikhil Abraham`,
img: `https://images-na.ssl-images-amazon.com/images/I/51RXaV0MGzL._SX397_BO1,204,203,200_.jpg`
},
{
id: 4,
title: `Learning React`,
author: `Alex Banks, Eve Porcello`,
img: `https://images-na.ssl-images-amazon.com/images/I/51FHuacxYjL._SX379_BO1,204,203,200_.jpg`
},
{
id: 5,
title: `Learning Web Design`,
author: `Jennifer Robbins`,
img: `https://images-na.ssl-images-amazon.com/images/I/51iVcZUGuoL._SX408_BO1,204,203,200_.jpg`
},
{
id: 6,
title: `JavaScript and JQuery: Interactive Front-End Web Development`,
author: `Jon Duckett`,
img: `https://images-na.ssl-images-amazon.com/images/I/41y31M-zcgL._SX400_BO1,204,203,200_.jpg`
},
{
id: 7,
title: `Head First JavaScript Programming`,
author: `Eric Freeman, Elisabeth Robson`,
img: `https://images-na.ssl-images-amazon.com/images/I/51qQTSKL2nL._SX430_BO1,204,203,200_.jpg`
},
{
id: 8,
title: `Learning Redux`,
author: `Daniel Bugl`,
img: `https://images-na.ssl-images-amazon.com/images/I/41gxBZ8GNpL._SX403_BO1,204,203,200_.jpg`
},
{
id: 9,
title: `Node.js 8 the Right Way`,
author: `Jim Wilson`,
img: `https://images-na.ssl-images-amazon.com/images/I/51t44mzlCaL._SX415_BO1,204,203,200_.jpg`
},
{
id: 10,
title: `PostgreSQL: Up and Running`,
author: `Regina Obe`,
img: `https://images-na.ssl-images-amazon.com/images/I/51FSjiYDfpL._SX379_BO1,204,203,200_.jpg`
},
{
id: 11,
title: `Fundamentals of Web Development`,
author: `Randy Connolly, Ricardo Hoar`,
img: `https://images-na.ssl-images-amazon.com/images/I/51xEzGTH6lL._SX402_BO1,204,203,200_.jpg`
},
{
id: 12,
title: `Web Design Playground`,
author: `Paul McFedries`,
img: `https://images-na.ssl-images-amazon.com/images/I/41-6F+RDbIL._SX258_BO1,204,203,200_.jpg`
}
]
export default data;
APP.JS
import React, {Component} from 'react'
import './App.css';
import BookList from './Components/BookList';
import Header from './Components/Header'
import Shelf from './Components/Shelf';
import data from './data'
class App extends Component {
constructor(){
super()
this.state ={
books : {data}
}
}
render(){
return (
<div className="App">
<Header/>
<BookList books={this.state.books}/>
<Shelf/>
</div>
);
}
}
export default App;
and BOOKLIST.JS:
import React, {Component} from 'react'
class BookList extends Component{
render(){
let mappedBooks = this.props.books.map(function(element){
return {element}
})
return(
<div className = 'BookList'>
<h1>list</h1>
</div>
)
}
}
export default BookList
in Data.js you've got
export default data;
and data is an array. So when you import data in App.js it's an array.
Try running this code snippet to get a better idea of what's going on here:
let data = [1,2,3,4]
console.log('when data is an array')
console.log('{data} is', {data})
data = {
'data': [1,2,3,4]
}
console.log('when data is an object with a property called data pointing to an array')
console.log('{data} is', {data})
console.log('but if you call a function and pass in data (as an object with data as a named property pointing to an array), you can use the curly braces to pull the array out of the object using destructuring')
function destructureData({data}) {
console.log(data)
}
destructureData(data)
So, when you do this:
this.state = {
books: { data }
}
it's actually an object property shorthand that's interpreted (in ES6) as this:
this.state = {
books: { data: data }
}
If you actually want this.state.books to be the array you imported from data.js then you set it directly:
this.state = {
books: data
}
Or, using that object property shorthand, and the fact that you've got data as a default export, you could change the import to this:
import books from './data'
And then do this:
this.state = {
books
}
If you'd like to read more about the destructuring syntax–where you use curly braces to pull data out of an object or an array–this article on MDN is a good read.
My component is supposed to retrieve the data for courses when the component mounts. The problem that I have is that whether I use the course Id or the course title as the key, I get the following error:
index.js:1 Warning: Each child in a list should have a unique "key" prop.
I have looked through the react docs, here on Stack Overflow, and tried different ways to get it to work. The only way I can get it to partially work is by adding an index as a parameter for map. When I use this method, I run into another problem and that is, it stops after the first iteration, even though there are 10 items. How can I fix this?
Here is my code:
CoursesPage.js
import React from 'react';
import { connect } from 'react-redux';
import * as courseActions from '../../redux/actions/courseActions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
class CoursesPage extends React.Component {
componentDidMount() {
this.props.actions.loadCourses().catch(error => {
alert("Loading courses failed" + error);
});
}
render() {
return (
<>
<h2>Courses</h2>
{this.props.courses.map((course, index) => (
<div key={course[index].title}>{course[index].title}</div>
))}
</>
);
}
}
CoursesPage.propTypes = {
courses: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}
function mapStateToProps(state) {
return {
courses: state.courses
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(courseActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage);
My mock data:
const courses = [
{
id: 1,
title: "Securing React Apps with Auth0",
slug: "react-auth0-authentication-security",
authorId: 1,
category: "JavaScript"
},
{
id: 2,
title: "React: The Big Picture",
slug: "react-big-picture",
authorId: 1,
category: "JavaScript"
},
{
id: 3,
title: "Creating Reusable React Components",
slug: "react-creating-reusable-components",
authorId: 1,
category: "JavaScript"
},
{
id: 4,
title: "Building a JavaScript Development Environment",
slug: "javascript-development-environment",
authorId: 1,
category: "JavaScript"
},
{
id: 5,
title: "Building Applications with React and Redux",
slug: "react-redux-react-router-es6",
authorId: 1,
category: "JavaScript"
},
{
id: 6,
title: "Building Applications in React and Flux",
slug: "react-flux-building-applications",
authorId: 1,
category: "JavaScript"
},
{
id: 7,
title: "Clean Code: Writing Code for Humans",
slug: "writing-clean-code-humans",
authorId: 1,
category: "Software Practices"
},
{
id: 8,
title: "Architecture Applications for the Real World",
slug: "architecting-applications-dotnet",
authorId: 1,
category: "Software Architecture"
},
{
id: 9,
title: "Becoming an Outlier: Reprogramming the Developer Mind",
slug: "career-reboot-for-developer-mind",
authorId: 1,
category: "Career"
},
{
id: 10,
title: "Web Component Fundamentals",
slug: "web-components-shadow-dom",
authorId: 1,
category: "HTML5"
}
];
const authors = [
{ id: 1, name: "Cory House" },
{ id: 2, name: "Scott Allen" },
{ id: 3, name: "Dan Wahlin" }
];
const newCourse = {
id: null,
title: "",
authorId: null,
category: ""
};
module.exports = {
newCourse,
courses,
authors
};
Edit:
I am using Redux Thunk.
Here is my actionType.js file:
export const CREATE_COURSE = "CREATE_COURSE";
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";
Here is my CourseActions.js file:
import * as types from './actionTypes';
import * as courseApi from "../../api/courseApi";
export function createCourse(course) {
return { type: types.CREATE_COURSE, course };
}
export function loadCourseSuccess(courses) {
return { type: types.LOAD_COURSES_SUCCESS, courses };
}
export function loadCourses() {
return function (dispatch) {
return courseApi.getCourses().then(courses => {
dispatch(loadCourseSuccess(courses));
}).catch(error => {
throw error;
})
}
}
Here is my courseReducer.js file:
import * as types from '../actions/actionTypes';
export default function courseReducer(state = [], action) {
switch (action.type) {
case types.CREATE_COURSE:
return [...state, { ...action.course }];
case types.LOAD_COURSES_SUCCESS:
return [...state, { ...action.courses }];
default:
return state;
}
}
Any help would be appreciated.
Thanks.
P.S. I know that you should use Id for key. But the way it has to be done for now is using the title of the course as the key.
With your edits, I think we can more effectively help you. For future, it would be beneficial to post an example of your code not working on https://codesandbox.io/
Also to help yourself out when you debug, isolate the react component from the use of redux. This will allow you to ensure your react component renders when given data, then focus on getting redux to provide you the data your react component has.
You can do this by first defining mockdata inside your react component, then moving that mock data to your reducer, then finally replacing the mock data with the live api call.
On to the code:
You have two issues: the first is that you want to index into the array courses but instead due to a typo, you are actually using the property accessor into the object course
key={course[index].title}
As your question states you must use the title as the key simply change the div to be:
<div key={course.title}>{course.title}</div> and your code should work as expected.
Once you have addressed that, then re-enable loading data from your API call using Redux, and you can address the issues with Redux.
Looking at your reducer, you have a clear bug, and depending on your use case, a potential bug:
case types.LOAD_COURSES_SUCCESS:
return [...state, { ...action.courses }];
action.courses is an array, and the code is creating a new array that contains all the elements of the previous array state and adding a new object, which contains the contents of a destructured array.courses array.
Which that does is effectively append a single object to your array, and the object is comprised of elements from your courses array. The item index becomes the key, and the item itself is the value.
You can visualize it here: https://codesandbox.io/s/divine-pond-nr3j8
Instead you want
return [...state, ...action.courses];
The second, potential bug, is that you are actually appending the results of the courses api. Which for subsequent calls to load courses, you will duplicate the data. That may be what you want, but I am going to assume that is not what you want.
So instead, your LOAD_COURSES_SUCCESS case should be rewritten to simply be:
return [...action.courses];
Since each course has a unique ID field, the problem could be solved by using the id as a key
render() {
return (
<>
<h2>Courses</h2>
{this.props.courses.map(course => (
<div key={course.id}>{course.title}</div>
))}
</>
);
}
The issue is here:
{this.props.courses.map((course, index) => (
<div key={course[index].title}>{course[index].title}</div>
))}
//course in itself is data
Solution:
{this.props.courses.map((course, index) => (
<div key={`${course.title}-${index}`}>{course.title}</div>
))}
Better way would always to take care unique id for key
Here's the code snippet for render method.
Give it a try.
*Update = use course title as key
render() {
const courseList = this.props.courses.map(course => (
<div key={course.title}></div>
));
return (
<>
<h2>Courses</h2>
<div>{courseList}</div>
</>
);
}
There are many ways you can pass the key when it is iterating...
var courses = this.state.courses.map(function(course, index) {
return(
<div key={index}>
<div key={course.title} id={course.title}>
<h2 key={"header"+course.title}>{course.title}</h2>
</div>
</div>
)
});
But you should try to pass a unique id rather than a string.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 4 years ago.
I'm learning React; I have a such problem. When I call function "handleSearch" it crashes with TypeError: Cannot read property 'CONTACTS' of undefined on console.log(this.CONTACTS);
I define CONTACTS in constructior. And call method handleSearch() in onChange of input. Then the error occurs.
But everything is ok with this.CONTACTS.map()
import React, { Component } from 'react';
import Contact from './Contact';
import './Contacts.css';
class ContactList extends Component {
constructor() {
super();
this.CONTACTS = [
{
id: 1,
name: 'Alex',
phone: '777',
image: 'https://78.media.tumblr.com/2b5293dc3133abeab79aa74e151e74f9/tumblr_pa1h148ulz1rzp45wo1_75sq.jpg'
},
{
id: 2,
name: 'Ann',
phone: '888',
image: 'https://78.media.tumblr.com/2b5293dc3133abeab79aa74e151e74f9/tumblr_pa1h148ulz1rzp45wo1_75sq.jpg'
},
{
id: 3,
name: 'Zel',
phone: '999',
image: 'https://78.media.tumblr.com/2b5293dc3133abeab79aa74e151e74f9/tumblr_pa1h148ulz1rzp45wo1_75sq.jpg'
}
];
this.state = {
displayedContacts: this.CONTACTS
};
}
handleSearch(event) {
var query = event.target.value.toLowerCase();
console.log(this.CONTACTS);
}
render() {
return (
<div className="contacts">
<input type="text" className="search-field" onChange={ this.handleSearch }/>
<ul className="contacts-list">
{
this.CONTACTS.map(function(el) {
return <Contact
key={el.id}
name={el.name}
phone={el.phone}
image={el.image} />
})
}
</ul>
</div>
);
}
}
export default ContactList;
You have to use bind in the constructor. In your event handler this refers to the DOM node. Please add this line to the constructor:
this.handleSearch = this.handleSearch.bind(this)
I came across such code in Javascript and became quite puzzled as I come from C++/Python background:
const list = [
{
title: 'React',
url: 'https://facebook.github.io/react/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
...
];
class App extends Component {
constructor(props) {
super(props);
# leanpub-start-insert
this.state = {
list: list,
};
# leanpub-end-insert
}
...
}
It seems like you can just use variable that is outside of a function. I understand that that is how JS works but I am not sure if that is what people usually do, just use variable from outside and not pass as parameter. Is that a standard and normal practice?
The following looks quite impossible to pass variable as parameter to function:
import React, { Component } from 'react';
import './App.css';
const list = [
{
title: 'React',
url: 'http://facebook.github.io/react',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://github.com/reactjs/redux',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
}
]
const isSearched = searchTerm => item =>
item.title.toLowerCase().includes(searchTerm.toLowerCase());
class App extends Component {
constructor(props) {
super(props);
this.state = {
list: list,
searchTerm: '',
}
this.onDismiss = this.onDismiss.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
}
onDismiss(id) {
const isNotID = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotID);
this.setState({ list: updatedList });
}
onSearchChange(event) {
this.setState({ searchTerm: event.target.value });
}
render() {
return (
<div className="App">
<form>
<input
type="text"
onChange={this.onSearchChange}
/>
</form>
{this.state.list.filter(isSearched(this.state.searchTerm)).map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={() => this.onDismiss(item.objectID)} type="button">
Dismiss
</button>
</span>
</div>
)}
</div>
)
}
}
export default App;
Is that a standard and normal practice?
Generally no, but there are exceptions. (for example having the entire state of the application in a variable).
Classes and functions are meant to be reused.
If your function (or class) relies on global variables then it will be hard to reuse and test.
Bottom line is: avoid it whenever possible.
In React it's definitely normal to have a list of fake data set in the state like in your example. You usually would import it from a different file though with the import syntax. But for better testability of your components, you are better off avoiding importing external libraries and instead passing it as parameters.
It's possible, but should be used with caution. As you can imagine, if everyone uses global variables, you can very easily end up with name collisions. Also, global variables are never garbage collected, so you risk littering the memory as well.
Generally, if you write a framework in JavaScript, you'll publish one global variable, which sort of serves as the "namespace" of your framework: Rx, d3, $, Handlebars, etc. These are usually the same as the name of the framework itself, so collisions are unlikely. Everything else is then defined within that object, e.g. Handlerbars.compile() and so on.