I'm new to react and only understand the basic. I got this project from someone to look at, but I'm scratching my head since morning with this problem: "Uncaught TypeError: this.state.persons.map is not a function".Please if you can try to try to go over it in easy but in under the hood way.Your help would be really appreciated.
App Component
class App extends Component {
state = { //not optional name state
persons: [
{ id: "43qtf3w4", name: 'Igor', age: 19},
{ id: "445wgwre", name: 'Vasya', age: 20},
{ id: "t45wg45e",name: "Petya", age: 22}
],
otherState: 'some other value',
showPersons: false,
}
switchNameHandler = (newName) => {
this.setState( {
persons: [
{ name: newName, age: 19},
{ name: 'Vasya', age: 20},
{ name: "Petya", age: 27}
]
} )
}
nameChangedHandler = (event, id) => {
const personIndex = this.state.persons.findIndex( (person) => person.id ===id);
const person = {...this.state.persons[personIndex]};
person.name = event.target.value;
const persons = {...this.state.persons};
persons[personIndex] = person;
this.setState( {
persons: persons
} )
}
deletePersonHandler = (personIndex) => {
const persons = [...this.state.persons];
persons.splice(personIndex, 1);
this.setState( {
persons: persons,
} )
}
togglePersonsHandler = () => {
const doesShow = this.state.showPersons;
this.setState( {
showPersons: !doesShow,
} )
}
render() {
const style = {
backgroundColor: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer',
}
let persons = null;
if ( this.state.showPersons) {
persons = (
<div >
{this.state.persons.map((person, index) => {
return <Person
click={() => this.deletePersonHandler(index)}
name={person.name}
age={person.age}
key={person.id}
changed={(event) => this.nameChangedHandler( event, person.id)}/>
})}
</div>
)
}
return (
<div className="App">
<h1>Hi, I'm a React App</h1>
<p>This is realy working</p>
<button
style={style}
onClick ={ this.togglePersonsHandler}>Toggle Persons</button>
{persons}
</div>
);
}
}
Person Component
const person = (props) => {
return (
<div className="Person">
<p onClick={props.click}>I'm a {props.name} and I am {props.age} years old!</p>
<p>{props.children}</p>
<input type="text" onChange={props.changed} value={props.name}/>
</div>
)
};
In the nameChangeHandler() function you are setting persons to an object:
const persons = {...this.state.persons}
which does not have .map()
Related
Here is my component:
const Card = ({ _name, emoji, logger }) => {
return (
<div style={styles.wrapper}>
<button onClick={logger}>-</button>
<h4>
{_name} {emoji}
</h4>
</div>
);
};
And here is my App.js:
const arr = [
{ _name: 'Apple', emoji: 'π', id: '01' },
{ _name: 'Banana', emoji: 'π', id: '02' },
{ _name: 'Peach', emoji: 'π', id: '03' },
{ _name: 'Pineapple', emoji: 'π', id: '04' },
{ _name: 'Mango', emoji: 'π₯', id: '05' },
{ _name: 'Melon', emoji: 'π', id: '06' },
];
const App = () => {
const logger = (el) => {
alert(el.target.value.emoji);
};
return (
<div style={styles.wrapper}>
{arr.map((item) => {
return (
<Card
_name={item._name}
emoji={item.emoji}
key={item.id}
logger={logger}
/>
);
})}
</div>
);
};
I'm trying to show the element emoji when you click on it. When I click on each item I got undefined. Why is that? What is the problem?
The parameter that gets passed to your click handler dosent't have a target.value.emoji, that's why you are getting undefined.
One way to do what you want is to change logger to this:
const logger = (emoji) => {
alert(emoji);
};
And Card component to:
const Card = ({ _name, emoji, logger }) => {
return (
<div style={styles.wrapper}>
<button onClick={e => logger(emoji)}>-</button>
<h4>
{_name} {emoji}
</h4>
</div>
);
};
I am able to map 3 objects as a normal list however how can I map it under the correct heading?
One way is to push each object to it's own array e.g. const employed = [] but it looks messy. Any suggestions for a better approach?
export const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
])
const items = list.map(({name, status}, index) => {
<Profile ... />
})
return (
<div>
<div>
<h1>Students</h1>
</div>
<div>
<h1>Employed</h1>
</div>
<div>
<h1>Unemployed</h1>
</div>
</div>
)
}
Keep another mapping to your statuses to header values and filter the list according to the statuses.
This would also work.
import { useState } from "react";
const STATUSES = {
student: "Studnets",
employed: "Employed",
unemployed: "Unemployed",
retired: "Retired"
};
const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
{ name: "baz", status: "student" }
]);
return (
<div>
{Object.entries(STATUSES).map(([statusKey, displayValue]) => {
const data = list
.filter(({ status }) => status === statusKey)
.map(({ name, status }, index) => <div>{name}</div>);
if (data.length > 0) {
return (
<div>
<h1>{displayValue}</h1>
{data}
</div>
);
}
})}
</div>
);
};
export default App;
Making three seperate lists using filter() would be one way to do it. Then you can show them as you need:
export const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
])
const students = list.filter(x => x.status === 'student' ).map(x => <Profile... />);
const employed = list.filter(x => x.status === 'employed' ).map(x => <Profile... />);
const unemployed = list.filter(x => x.status === 'unemployed' ).map(x => <Profile... />);
return (
<div>
<div>
<h1>Students</h1>
{students}
</div>
<div>
<h1>Employed</h1>
{employed}
</div>
<div>
<h1>Unemployed</h1>
{unemployed}
</div>
</div>
)
}
Getting the error: TypeError: Cannot read property 'state' of undefined when I tried to render the elements with togglePersonsHandler to get the content which is inside the personsState. The goal is to get the contents by clicking on the button.
Here is my App.js file
import React, { useState } from 'react';
import './App.css';
import Person from './person/Person';
const App = props => {
const [ personsState ] = useState({
persons: [
{ name: 'Maxmillian', age: 28 },
{ name: 'Manu', age: 18 },
{ name: 'Stephanie', age: 24 }
],
otherState: 'some other value',
showPersons: false
});
const switchNameHandler = (newName) => {
// console.log('Was clicked!');
this.setState({
persons: [
{ name: newName, age: 28 },
{ name: 'Manu', age: 18 },
{ name: 'Stephanie', age: 24 }
]
});
};
const nameChangedHandler = (event) => {
this.setState({
persons: [
{ name: 'Max', age: 28 },
{ name: event.target.value, age: 18 },
{ name: 'Stephanie', age: 24 }
]
})
};
const togglePersonsHandler = () => {
const doesShow = this.state.showPersons;
this.setState({showPersons: !doesShow});
}
const style = {
backgroundColor: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer'
};
return (
<div className="App">
<h1>Hi, I'm a React App</h1>
<p>This is really working!</p>
<button
style={style}
onClick={togglePersonsHandler}>Switch Name</button>
{
this.state.showPersons === true ?
<div>
<Person
name={personsState.persons[0].name}
age={personsState.persons[0].age} />
<Person
name={personsState.persons[1].name}
age={personsState.persons[1].age}
click={switchNameHandler.bind(this, 'Max!')}
changed={nameChangedHandler}>My Hobbies: Racing</Person>
<Person
name={personsState.persons[2].name}
age={personsState.persons[2].age} />
</div> : null
}
</div>
);
}
// ein Element entwickeln
// return React.createElement('div', {className: 'App'}, React.createElement('h1', null, 'Work'));
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
And here the Person.js
import React from 'react';
import './Person.css';
const person = (props) => {
return (
<div className="Person">
<p onClick={props.click}>I'm {props.name} and I am {props.age} years old. </p>
<p>{props.children}</p>
<input type="text" onChange={props.changed} value={props.name} />
</div>
)
};
export default person;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
Thank you for the support!
this.state and this.setState is only available in class components extending React.component. You're using a functional component, therefore you need to use hooks to update the state. I suggest to create a separate state for showPersons:
const [ showPersons, setShowPersons ] = useState(false);
const togglePersonsHandler = () => {
setShowPersons(!showPersons);
}
If you only want one state, add a setter:
const [ personsState, setPersonsState ] = useState({...});
the array is stored in local storage by clicking on the βMoreβ button 2 news is added to DOM
How should I implement this?
const ARR= [
{
id: 1,
Name: 'Name1',
text:'lorem ipsum'
},
{
id: 2,
Name: 'Name2',
text:'lorem ipsum'
},
{
id: 3,
Name: 'Name3',
text:'lorem ipsum'
},
{
id: 4,
Name: 'Name4',
text:'lorem ipsum'
},
];
10 obj
here we save the array in localStorage
and where to execute its JSON.parse (localStorage.getItem ('news')) and I donβt understand how to implement work with local storage by click
```
localStorage.setItem('news', JSON.stringify(ARR));
class NewsOne extends PureComponent {
constructor() {
super();
this.state = {
listItem: ARR,
formAdd: false,
};
this.createItem = this.createItem.bind(this);
this.updateItem = this.updateItem.bind(this);
this.removeItem = this.removeItem.bind(this);
this.addForm = this.addForm.bind(this);
}
updateItem(item) {
const { listItem } = this.state;
this.setState({
listItem: listItem.map(elem => (
elem.id === item.id ? item : elem
))
});
}
removeItem(itemId) {
const { listItem } = this.state;
this.setState({
listItem: listItem.filter(item => item.id !== itemId)
});
}
createItem(item) {
const { listItem } = this.state;
this.setState({
listItem: [item, ...listItem],
});
}
addForm() {
const { formAdd } = this.state;
this.setState({
formAdd: !formAdd,
})
}
render() {
const { listItem, formAdd } = this.state;
return(
<>
<div className="box">
<Title />
<List
data={listItem}
removeFromProps={this.removeItem}
updateFromProps={this.updateItem}
/>
</div>
<button className ="addnews" onClick = {this.addForm}>
Add
</button>
</>
);
}
}
A class that defaultly displays 2 elements on page.
I tried here to interact with the array from localStorage, but it fails
class List extends PureComponent {
constructor() {
super();
this.state = {
count: 2,
}
this.addObj = this.addObj.bind(this);
}
addObj() {
const { count } = this.state;
this.setState({
count: count + 2,
});
}
render() {
const { count } = this.state;
const { data } = this.props;
return(
<>
<ul>
{
data.slice(0, count).map(item => (
<>
<Item
key={item.id}
item={item}
/>
</>
))
}
</ul>
<button className ="addnews" onClick = {this.addObj}>
More
</button>
</>
);
}
}
Expected effect:
click button -> call function save -> pass object p to function update
update second object{a: 'purple', desc: 'grt', date: '12 -10-2019 '} in colors array, which is in theproducts array
Before update: {a: 'purple', desc: 'grt', date: '12 -10-2019 '}
After update: {a: 'violet', desc: 'gt', date: '12 -12-1980 '}
Error in console.log:
Uncaught TypeError: this.props.product.colors.map is not a function
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}]
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a: 'white', {a:'black', desc: 'grtrt', date: '12-12-2119'}, }, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0 //It is first object in products array
index: 1 //It is second object in colors array
}
}
update = (item) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: item}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
Items
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
</div>
);
}
}
Item
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
You need to pass the index of the colors item and then update it accordingly
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
and then in the topmost parent
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorsIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
Working demo
const { Component } = React;
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}],
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a:'black', desc: 'grtrt', date: '12-12-2119'}, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0,
index: 1
}
}
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
}
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
);
}
}
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<pre>{JSON.stringify(this.props.time)}</pre>
<button onClick={this.save}>Save</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />