I'm going through CodeCademy React beginner training. I just finished a short chapter on props and passing props, and I'm still very confused on the topic and wanted to do some more reviewing on this. CodeCademy had the below question as a "quiz", I submitted my answer - it told me it was wrong and didn't give me an explanation or solution.
Could someone please answer this question for me and maybe provide me a good guide to review the react props.
Question: Pass in the item, quantity, price of an item as props from the App component to the shopping cart component. These values will be rendered within the corresponding list elements. The item prop should contain the string “Apple”, the quantity prop should contain the number 3, and the price prop should contain the number 1.99.
import React from 'react';
import ReactDOM from 'react-dom';
export class App extends React.Component {
render() {
return (
<ShoppingCart />
);
}
}
export class ShoppingCart extends React.Component {
render() {
return <ul>
<li>Item: {this.props.item}</li>
<li>Quantity: {this.props.quantity}</li>
<li>Price: {this.props.price}</li>
</ul>;
}
Thank you
If I understand well, the shopping cart should expect an array of items, ie: <ShoppingCart items={[]} />. Then inside the shopping cart, you map over the received items.
Then, I think the excepted answer should look like this:
import React from 'react';
export class App extends React.Component {
render() {
return (
<ShoppingCart items={[{ item: 'Apple', quantity: 3, price: 1.99 }]} />
);
}
}
export class ShoppingCart extends React.Component {
render() {
return (
<div>
{this.props.items.map(item => (
<ul key={JSON.stringify(item)}>
<li>Item: {item.item}</li>
<li>Quantity: {item.quantity}</li>
<li>Price: {item.price}</li>
</ul>))
}
</div>
);
}
You are not passing the props to the ShoppingCart component when trying to render it from App class.
do the below, to correct it
export class App extends React.Component {
render() {
return (
<ShoppingCart item="Apple" quantity={3} price={1.99} />
);
}
}
Related
I need a little help. I'm working on a project that uses Class Components in React and I got stuck with a issue.
How can I pass datas using props?
For example, imagine that I have one Component that have an array in the state:
import React,{Component} from "react";
class CarList extends Component{
constructor(props){
super(props);
this.state = {
carList: ['Jeep', 'Kwid','HB20','Ônix', 'Prisma', 'Gol quadrado']
}
}
render(){
return(
<div>
</div>
);
}
}
export default CarList;
And now I have to call this array in a Option Tag inside a Select Tag.
Let's imagine this component Bellow:
import React from "react";
import { Component } from "react";
import CarList from "./components/Datas";
class App extends Component{
render(){
return(
<div>
<p>I got it! Here is the car list:</p>
<select>
{this.state.CarList.map( (item,x)=>{
return(
<option key={x}>{item}</option>
)
})}
</select>
</div>
)
}
}
export default App;
This piece of code does not work.
the console.log says: "Uncaught TypeError: this.state is null"
I know that I could create a div with my datas and call with , but I have to use props to pass the datas between the Components.
How can I create a callback function using props to resolve this?
Hi!
I tried to call using this.state, but I got "this.state is not defined"
To pass your data as a props you have to pass it to your child component like this from your parent component :
class ParentComponent extends React.Component {
state = {
carList: [],
};
constructor(props) {
super(props);
this.state = {
carList: ['Jeep', 'Kwid', 'HB20', 'Ônix', 'Prisma', 'Gol quadrado'],
};
}
render() {;
return (
<div>
<ChildComponent carList={this.state.carList || []} />
</div>
);
}
}
and then it is accessible in your child component with this.props.
you can use this props in child component like this:
class ChildComponent extends React.Component {
render() {
return (
<div>
{this.props.carList.map((cars, index) => {
return (
<span key={index}>
{cars}
<br />
</span>
);
})}
</div>
);
}
}
Edit -
if you want to see source code : https://stackblitz.com/edit/react-ts-ddcylu?file=Parent.tsx
I have a question that I believe that is simple, buuut I couldn't solve it and I didn't find something similar on the internet.
I have a React Component that have an array like state and I'm trying to take this array and put in another array into a select-option.
This is the Component with the array:
import React,{Component} from "react";
class CarList extends Component{
constructor(props){
super(props);
this.state = {
carList: ['Jeep', 'Kwid','HB20','Ônix', 'Prisma', 'Gol quadrado']
}
}
render(){
return(
<div>
<select>
{this.state.carList.map( (item,ii)=>{
return(
<option key={ii}>{item}</option>
)
} )}
</select>
</div>
);
}
}
export default CarList;
And this is the Component that is render on React-dom:
import React from "react";
import { Component } from "react";
import CarList from "./components/Tabela";
class App extends Component{
constructor(props){
super(props);
this.state={
}
}
render(){
return(
<div>
<p>I got it! Here is the car list:</p>
<select>
{this.state.carList.map( (item,x)=>{
return(
<option key={x}>{item}</option>
)
})}
</select>
</div>
)
}
}
export default App;```
Hey!
I tried to call with a map();
I tried to import the component;
I tried to call the array just before import the component;
Not sure if I understand you correctly. Here is an option:
From checking your App component code, the select component would give the same result as this:
render() {
return (
<div>
<p>I got it! Here is the car list:</p>
<CarList />
</div>
);
}
I'm kinda new to react and thought that in the constructor function, using super(props) can fully receive the props that passed by parents. But however, I can't get the string test from my parent component.
So in the parent component, I pass the string "test" as props
import React from 'react';
import Post from '../components/post';
import "../components/css/post.css"
class Bulletin extends React.Component {
render()
{
console.log(this.props);
return (
<div>
<Post test={"sent from parent"}/>
</div>
);
}
}
export default Bulletin;
And then in Post.js, I print the props in two places:
import React, { Component } from 'react';
export default class Edit extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
console.log(this.props);
return (
<div className="editor" onClick={this.focus}>
</div>
);
}
}
The two outputs are both {className: "editor"} which is not what I need. I need the string {test: "sent from parent"} and don't know why this doesn't works for me.
I'm just getting started with React and ES6 and I am trying to DRY up my app a bit. What I'm looking to do is create a component that takes a collection and an attribute of the items in that collection and turn it into a list. For example, if I pass in a group of authors and specify last name, it will create a list of the authors' last names; using the same component I would like to use it elsewhere and pass in a group of songs and list them out by title.
Here's what I have so far:
ItemList:
import PropTypes from 'prop-types'
import React from 'react'
import Item from './Item'
export default class ItemList extends React.Component {
constructor(props){
super(props)
}
render() {
let items
if(this.props.items.length === 0){
items = <div>Nothing Found</div>
} else {
items = this.props.items.map(item => {
return(
<Item item={item} displayAttribute={this.props.displayAttribute}
)
});
}
return (
<div className="item-index">
<div className="list-group">{items}</div>
</div>
);
}
}
Item:
import PropTypes from 'prop-types'
import React from 'react'
export default class Item extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div className="list-group-item" data-index={this.props.item.id}>
<div className="item-attribute">
*This is where I want to print the item's display attribute*
</div>
</div>
);
}
}
Elsewhere in the app, I would like to be able to call something like the following:
<ItemList items={this.state.authors} displayAttribute="last_name" />
or
<ItemList items={this.state.songs} displayAttribute="title" />
and the component would create a list using the specified attribute
If I understood you correctly this should do (in your item list):
<div className="item-attribute">
{this.props.item[this.props.displayAttribute]}
</div>
I'm having problems with my first React application.
In practice, I have a hierarchy of components (I'm creating a multimedia film gallery) which, upon clicking on a tab (represented by the Movie component) must show the specific description of the single film (SingleMovieDetails).
The problem is that the DOM is updated only on the first click, then even if the SingleMovieDetails props change, the DOM remains locked on the first rendered movie.
Here's the code i wrote so far...
//Movie component
import React from "react";
import styles from "./Movie.module.scss";
import PropTypes from "prop-types";
class Movie extends React.Component{
constructor(props){
super(props);
this.imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`;
}
render(){
if(!this.props.size)
return <div onClick={this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDiv}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
return <div onClick={() => this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDivBig}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
}
}
Movie.propTypes = {
movie: PropTypes.any,
callbackClick: PropTypes.any
};
export default Movie;
SingleMovieDetails.js
import React from "react";
import styles from "./SingleMovieDetails.module.scss";
import Movie from "../Movie";
import SingleMovieDescription from "../SingleMovieDescription";
import MovieCast from "../MovieCast";
import SingleMovieRatings from "../SingleMovieRatings";
class SingleMovieDetails extends React.Component{
constructor(props){
super(props);
console.log(props);
this.state = props;
console.log('constructor', this.state.movie)
}
render(){
console.log('SMD', this.state.movie)
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.state.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.state.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
export default SingleMovieDetails;
MovieCarousel.js
import React from "react";
import PropTypes from "prop-types";
import Movie from "../Movie";
import styles from "./MovieCarousel.module.scss";
import SingleMovieDetails from "../SingleMovieDetails";
class MovieCarousel extends React.Component {
constructor(props) {
super(props);
this.state = [];
this.callbackClickMovie = this.callbackClickMovie.bind(this);
}
callbackClickMovie(id) {
const singleMovieApi = `https://api.themoviedb.org/3/movie/${id}?api_key=b6f2e7712e00a84c50b1172d26c72fe9`;
fetch(singleMovieApi)
.then(function(response) {
return response.json();
})
.then(data => {
this.setState({ selected: data });
});
}
render() {
let details = null;
if (this.state.selected) {
details = <SingleMovieDetails movie={this.state.selected} />;
}
let counter = 6;
let movies = this.props.movies.map(el => {
let element = (
<Movie movie={el} callbackClick={this.callbackClickMovie} />
);
counter--;
if (counter >= 0) return element;
return;
});
let content = (
<>
<h2 className={styles.carouselTitle}>{this.props.title}</h2>
{movies}
{details}
</>
);
return content;
}
}
MovieCarousel.propTypes = {
children: PropTypes.any
};
export default MovieCarousel;
I would be really grateful if someone could help me. I have been on it for two days but I can't really deal with it
This is because in SingleMovieDetails component, you are storing the props values in state and not updating the state on props change. constructor will not get called again so state will always have the initial values.
You have two options to solve the issue:
Directly use the props values instead of storing data in state (preferred way). Like this:
class SingleMovieDetails extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.props.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.props.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
Use getDerivedStateFromProps, and update the state value on props change.
Same issue with Movie component also, put this line in the render method, otherwise it will always show same image:
const imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`
And use this imgUrl variable.
your Problem is just related to one file: SingleMovieDetails.js
Inside the constructor you´re setting the component state to get initialized with the props (send to the component the first time)
But inside your render() method you are referencing that state again:
<Movie size={'big'} movie={this.state.movie}/>
All in all thats not completely wrong, but you need to do one of two things:
Add a method to update your component state with the nextPropsReceived (Lifecycle Method was called: will receive props, if you are using the latest version you should use: getDerivedStateFromProps)
preferred option: you dont need a state for the movie component, so just use the props inside the render function (this.props.movie)
afterwards you can also delete the constructor, because there is nothing special inside. :)
edit:
So, just to be clear here: Since you´re only setting the state once (the constructor is not called on every lifecycle update) you will always only have the first value saved. Changing props from outside will just trigger render(), but wont start the constructor again ;D