I am trying to implement Presentational and Container Components pattern when creating React components. So I created a presentational component with only UI elements and container component with handling data capabilities.
component.jsx
import React from "react";
const MyComponent = ({props}) => (
<div>
{props.games.map((game, index) => (
<div key={index}>
{game.index + 1} - {game.contestName}
</div>
))};
</div>
);
export default MyComponent;
container.jsx
import React, { Component } from "react";
import MyComponent from "./component";
class MyContainer extends Component {
constructor(props) {
super(props);
this.state = {
games: [
{
id: 1,
categoryName: "Business/Company",
contestName: "Name1"
},
{
id: 2,
categoryName: "Magazine/Newsletter",
contestName: "Name2"
},
{
id: 3,
categoryName: "Software Component",
contestName: "Name3"
},
{
id: 4,
categoryName: "Website",
contestName: "Name4"
}
]
};
}
render() {
return (
<div>
<MyComponent games={this.state.games} />
</div>
);
}
}
export default MyContainer;
However, I can not render data and I get
Uncaught TypeError:
Cannot read property 'games' of undefined.
Would really appreciate your help, as two days of internet digging has not yielded positive results.
const MyComponent = ({props}) => (
When you do this, you actually do
{ props: props-received-from-parent }
You are enclosing your props in another object, remove those braces and change that line to
const MyComponent = (props) => (
and you are good to go.
You should destructure your games instead of props:
import React from "react";
const MyComponent = ({games}) => (
<div>
{games.map((game, index) => (
<div key={index}>
{game.index + 1} - {game.contestName}
</div>
))};
</div>
);
export default MyComponent;
You can define your MyComponent class like this
class MyComponent extends Component{
render(){
this.xyz = this.props.games.map((item,index) => {
return(<div key={index}>{item.id}</div>)
})
return(
<div>
{this.xyz}
</div>
)
}
}
export default MyComponent;
This will also work!
Related
I'm learning React. I'm trying to build a simple todo app on my own to learn & practice with the library. I have passed a list of tasks in the parent component & passed them to the child component as props. I was also able to output it in the child component using the map() method. However, I have no idea how to delete an item. I have tried searching online, but I'm unable to adapt their solutions to my use case.
Parent Component
import React, { Component } from 'react';
import './styles/components/App.css';
import Todos from './components/Todos'
class App extends Component {
state = {
todos: [
{ task: 'Study React', id: 1 },
{ task: 'Build something with it', id: 2 },
{ task: 'Apply for jobs', id: 3 },
],
}
render(){
return (
<div className="App">
<Todos todos={this.state.todos} />
</div>
);
}
}
export default App;
Child Component
import React, { Component } from 'react';
import '../styles/components/Todos.css'
class Todos extends Component {
render() {
let { todos } = this.props;
let todoList = todos.map(( todo => {
return (
<div className="todos" key={todo.id}>
<div>{ todo.task }</div>
</div>
)
}));
return (
<div onClick={this.deleteTask}>{ todoList }</div>
)
}
deleteTask() {
// checks if method is working
console.log('working');
// code to delete
}
}
export default Todos
Parent Component
import React, { Component } from 'react';
import './styles/components/App.css';
import Todos from './components/Todos'
class App extends Component {
state = {
todos: [
{ task: 'Study React', id: 1 },
{ task: 'Build something with it', id: 2 },
{ task: 'Apply for jobs', id: 3 },
],
};
// Event and data put in same Component.
deleteTodo(id) {
let workTodos = [...this.state.todos];
const deleteIndex = workTodos.findIndex(todo => todo.id === id);
workTodos.splice(deleteIndex, 1);
this.setState({
todos: [...workTodos]
})
}
render(){
// Use props give Todos Component the data and events to render dom
return (
<div className="App">
<Todos
todos={this.state.todos}
deleteTodo={this.deleteTodo.bind(this)}
/>
</div>
);
}
}
export default App;
Child Component
import React, { Component } from 'react';
import '../styles/components/Todos.css'
class Todos extends Component {
render() {
// Receiving events and data form props
let { todos, deleteTodo } = this.props;
// Click div trigger deleteTodo, It can execute deleteTodo in App component
return todos.map(( todo => {
return (
<div
className="todos"
key={todo.id}
onClick={() => { deleteTodo(todo.id) }}
>
<div>{ todo.task }</div>
</div>
)
}));
}
}
export default Todos
Like a commit, put delete event in App component, Then use props trigger it in the Todos component, Please let me know if you have any questions.
I am following a tutorial. I don't get why totalCounters is null. I searched online but I do not understand it.
The error message I get is :
TypeError: Cannot read property 'counters' of null.
I followed the tutorial from Mosh.
This is my App.js file.
import React, { Component } from "react";
import NavBar from "./components/navbar";
import Counters from "./components/counters";
import "./App.css";
class App extends Component {
render() {
return (
<React.Fragment>
<NavBar totalCounters={this.state.counters.length} />
<main className="container">
<Counters
counters={this.counters}
onReset={this.handleReset}
onIncrement={this.handleIncrement}
onDelete={this.handleDelete}
/>
</main>
</React.Fragment>
);
}
}
export default App;
This is my navbar.jsx
import React, { Component } from "react";
class NavBar extends Component {
render() {
return (
<nav className="navbar navbar-light bg-light">
<a className="navbar-brand" href="#">
Navbar <span className="badge badge-pill badge-secondary">{this.props.totalCounters}</span>
</a>
</nav>
);
}
}
export default NavBar;
This is my counters.jsx
import React, { Component } from "react";
import Counter from "./counter";
class counters extends Component {
state = {
counters: [
{ id: 1, value: 5 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 }
]
};
handleIncrement = counter => {
const countersCopy = [...this.state.counters];
const index = countersCopy.indexOf(counter);
countersCopy[index] = { ...counter };
countersCopy[index].value++;
this.setState({ counters: countersCopy });
};
handleReset = () => {
const resetCounters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters: resetCounters });
};
handleDelete = counterId => {
const newCounters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters: newCounters });
};
render() {
return (
<div>
<button
onClick={this.handleReset}
className="btn btn-primary btn-sm m2"
>
Reset
</button>
{this.state.counters.map(counter => (
<Counter
key={counter.id}
onDelete={this.props.onDelete}
onIncrement={this.handleIncrement}
counter={counter}
/>
))}
</div>
);
}
}
export default counters;
In React, this.state is local to each component.
So, setting this.state.counters in counters does not allow App component to use the state.
This is why counters is null in App component.
Because you don't have a state field into your App class components.
Everywhere you want to use state, you have to create a state object.
Class field
class App extends Component {
state = { counters: [] }
}
Inside contructor
class App extends Component {
contructor(props) {
super(props)
this.state = { counters: [] }
}
}
You are not initializing the state. Your state is undefined. Fix it like this
class App extends Component {
this.state = { counters : [] }
}
I try to learn React and I have an issue. I want to make an example myself from the tutorial.
import React, { Component } from 'react';
class MyComponent extends Component {
state = {
persons: [],
firstPersons: 5,
variables: {
Id: '1',
first: this.firstPersons,
input: {},
}
}
render() {
console.log('this.state', this.state);
return (
<div>
<p>Hello React!!!</p>
</div>
);
}
}
export default MyComponent;
I put in render method a console.log(this.state).
I have this simple state in my page, and when I run the project I get the error:
Uncaught TypeError: Cannot read property 'firstPersons' of undefined
Please someone tell me what is wrong on my code?
You can't access the object inside of itself in JS. You should do:
import React, { Component } from 'react';
var myVar = 5;
class MyComponent extends Component {
state = {
persons: [],
firstPersons: myVar,
variables: {
Id: '1',
first: myVar,
input: {},
}
}
render() {
console.log('this.state', this.state);
return (
<div>
<p>Hello React!!!</p>
</div>
);
}
}
export default MyComponent;
or
import React, { Component } from 'react';
class MyComponent extends Component {
state = {
persons: [],
firstPersons: 5,
variables: {
Id: '1',
input: {},
}
}
componentWillMount() {
this.state.variables.first = this.state.firstPersons;
this.setState(this.state);
}
render() {
console.log('this.state', this.state);
return (
<div>
<p>Hello React!!!</p>
</div>
);
}
}
export default MyComponent;
or if componentWillMount() is deprecated
import React, { Component } from 'react';
class MyComponent extends Component {
state = {
persons: [],
firstPersons: 5,
variables: {
Id: '1',
input: {},
}
}
static getDerivedStateFromProps(props, state) {
this.state.variables.first = this.state.firstPersons;
this.setState(this.state);
}
render() {
console.log('this.state', this.state);
return (
<div>
<p>Hello React!!!</p>
</div>
);
}
}
export default MyComponent;
I suggest you this syntax :
import React, { Component } from 'react';
class MyComponent extends Component {
constructor() {
super();
this.state = {
persons: [],
firstPersons: 5,
variables: {
Id: '1',
first: this.firstPersons,
input: {},
}
}
render() {
console.log('this.state', this.state);
return (
<div>
<p>Hello React!!!</p>
</div>
);
}
}
export default MyComponent;
I am an extreme newcomer to development, and as such I might be missing something obvious. I am busy creating a simple React app to display an array of objects. I have hard coded the general idea of an array in the parent component and passed it through to the child components. However, when I try logging the prop to console, it first appears as the object should, then logs again as undefined. This is making it really difficult to call an Object.keys() function on it and try map it to an array.
Below is an example of the code:
export class Parent extends React.Component() {
constructor(props) {
super(props);
this.state = {
arr: []
}
}
render() {
return (
<div>
<child array={this.state.arr} />
</div>
)
}
}
Then the child follows:
export class Child extends React.Component() {
renderArray() {
let arr = this.props.array;
console.log(arr);
return Object.keys(arr).map(arrayItem => {
let title = arr[arrayItem];
return <li key={title.id}> {title.name}</li>
}
}
render() {
return (
<div>
{this.renderArray()}
</div>
)
}
}
If I call console.log on arr object in the child component, it returns the props correctly. But then right after that it logs a second instance of 'undefined'.
EDIT: Lots of comments about empty array. Please see code below.
Here is the parent component.
import React, { Component } from 'react';
import './App.css';
import { SearchBar } from '../SearchBar/SearchBar.js';
import { SearchResults } from '../SearchResults/SearchResults.js';
import { Playlist } from '../Playlist/Playlist.js';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
searchResults: [
{
"id": 2011,
"name": 'What Makes A Man',
"artist": 'Man As Machine',
"album": 'Nothing but a thing'
},
{
"id": 2056,
"name": 'Pushpin',
"artist": 'Man As Machine',
"album": 'Patterns'
},
{
"id": 2099,
"name": 'Zombie',
"artist": 'Man As Machine',
"album": 'Patterns'
}
],
playlistName: ''
}
}
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults}/>
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
Then I have the searchResults component (child):
import React from 'react';
import './SearchResults.css';
import { Tracklist } from '../Tracklist/Tracklist.js';
export class SearchResults extends React.Component {
render () {
return (
<div className="SearchResults">
<h2>Results</h2>
<Tracklist tracks={this.props.searchResults}/>
</div>
)
}
}
and finally the tracklist component:
import React from 'react';
import './Tracklist.css';
import { Track } from '../Track/Track.js';
export class Tracklist extends React.Component {
constructor(props) {
super(props);
}
renderTrackList() {
let tracks = this.props.tracks;
console.log(tracks);
return Object.keys(tracks).map(track => {
let trackItem = tracks[track];
return <Track key={trackItem.id} track={trackItem}
})
}
render () {
return (
<div className="TrackList">
{this.renderTrackList()}
</div>
)
}
}
Here is the searchBar component:
import React from 'react';
import './SearchBar.css';
export class SearchBar extends React.Component {
render() {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<a>SEARCH</a>
</div>
);
}
}
and here is the Track component:
import React from 'react';
import './Track.css';
export class Track extends React.Component {
renderAction (isRemoval) {
if (this.props.isRemoval){
return <a className="Track-action" onClick={this.removeTrack}>-</a>
} else {
return <a className="Track-action" onClick={this.addTrack}>+</a>
}
}
render () {
return (
<div className="Track">
<div className="Track-information">
<h3>{this.props.track.name}</h3>
<p>{this.props.track.artist} | {this.props.track.album}</p>
</div>
<a className="Track-action">{this.renderAction}</a>
</div>
)
}
}
you're working with an array of something, and Object.keys() is for iterate on object property keys.
You can iterate in your array directly with map().
return arr.map(arrayItem => {
let title = arr[arrayItem];
return <li key={title.id}> {title.title}</li>
})
Or you can do it better because here you are missing the <ul> element
export class Child extends React.Component() {
renderArray() {
if(this.props.array && this.props.array.length >0){
return (
<ul>
{this.props.array.map(item => <li key={item.id}>{item.title}</li>)
</ul>
)
}
return 'No item in array.'
}
render() {
return (
<div>
{this.renderArray()}
</div>
)
}
}
You've to put some data in your array
this.state={
arr:[
{id:1,title:"item 1"},
{id:2,title:"item 2"}
]
}
Child component be Child but not child when you call it. Please try below solution
import Child from “./Child”;
export class Parent extends React.Component() {
constructor(props) {
super(props);
this.state = {
arr: [
{"id":1, name:"test"}, {"id": 2, "name": "test2"}
]
}
}
render() {
return (
<div>
<Child array={this.state.arr} />
</div>
)
}
}
Child component
export class Child extends React.Component() {
renderArray = (array) => {
return array.map(item=> (
<li key={item.id}> {item.name}</li>
)
}
render() {
const {array} = this.props;
return (
<div>
<ul>
{this.renderArray(array)}
</ul>
</div>
)
}
}
Please excuse me for wrong double quotes because I am answering on my phone
How I can add component CountryName and component CountryCapital to component Country ?
Display lists in browser:
Russia
Moscow
France
Paris
data.js
export default [{
id: 1,
name: 'France',
capital: 'Paris',
},
{
id: 2,
name: 'Russia',
capital: 'Moscow'
}];
First component CountryName.js
import React, { Component } from 'react'
class CountryName extends Component {
render() {
const {data} = this.props;
const CountryName = data.map(country => {
return (
<div>
<h2>{country.name}</h2>
</div>
)
})
return (
<div>
{CountryName}
</div>
);
}
}
export default CountryName;
Second component CountryCapital.js
import React, { Component } from 'react'
class CountryCapital extends Component {
render() {
const {data} = this.props;
const CountryCapital = data.map(country => {
return (
<div>
<p>{country.capital}</p>
</div>
)
})
return (
<div>
{CountryCapital}
</div>
);
}
}
export default MovieDescription;
Third component Country.js
import React, { Component } from 'react'
import CountryName from './CountryName';
import CountryCapital from './CountryCapital';
class Country extends Component {
render() {
const {data} = this.props;
const country = data.map(country => {
return (
<li key = {country.id}>
</li>
)
})
return (
<ul>
{Country}
</ul>
);
}
}
export default Country;
App.js
import React, {Component} from 'react';
import Country from './components/Country';
class App extends Component {
render() {
return (
<div>
<Country data={this.props.data}/>
</div>
)
}
}
export default App;
//HTML:
<body>
<div id="root"></div>
</body>`
First of all your components are structured in a way that, it will first list down all country names one by one and then list down country capitals one by one.
Since your requirement is to display Country and its capital as a group (like listed together in each line), you don't need two components. And in your Country component, just call the newly written component to display it as a group.
Something like this will work. Combine CountryName and CountryCapital to a single component (CountryData) like below.
const {data} = this.props;
const CountryData = data.map(country => {
return (
<div>
<div><h2>{country.name}</h2></div>
<div><h2>{country.capital}</h2></div>
</div>
)
})
return (
<div>
{CountryData}
</div>
);
And in your Component Country, call this new component and pass props like:
import CountryData from './CountryData';
......
......
render() {
const {data} = this.props;
return (
<CountryData data={this.props.data} />
);
}
Hope that helps.
Error: JSX elements must be wrapped in an enclosing tag
return (
<div><h2>{country.name}</h2></div>
<div><h2>{country.capital}</h2></div>
^
)
})