I'm following Traversey Media's React crash course, and I wanted to extend what he built to save TodoItems to a Firebase realtime database. The TodoItems save just fine, but do not appear in the UI.
I've tried having the app start with a hard-coded TodoItem in case it was an issue of having an empty array, but this does not help anything.
relevant methods:
(full code available at https://github.com/etothepi16/react-todo)
// Delete todo item
delTodo = (id) => {
fire.database().ref(`todos/${id}`).remove()
.then(function(){
this.setState({
// Loop through todos array and filter out item with provided ID
// ... is the spread operator, used here to copy the todos array
todos: [...this.state.todos.filter((todo) => todo.id !== id)]
})
.catch(function(error){
console.log("Remove failed: " + error.message);
})
});
};
// Add todo
addTodo = (title) => {
let id = uuid.v4();
let database = fire.database();
let todosRef = database.ref(`todos/${id}`);
let newTodo = {
id: this.id,
title: title,
completed: false
}
todosRef.set(newTodo).then(
this.setState({todos: [...this.state.todos, newTodo]})
);
};
render() {
return (
<Router>
<div className='App'>
<div className='container'>
<Header />
<Route
exact
path='/'
render={(props) => (
<React.Fragment>
<AddTodo addTodo={this.addTodo} />
<Todos
todos={this.state.todos}
markComplete={this.markComplete}
delTodo={this.delTodo}
/>
</React.Fragment>
)}
/>
<Route path='/about' component={About} />
</div>
</div>
</Router>
);
}
};
class Todos extends Component {
render(){
return this.props.todos.map((todo)=>(
<TodoItem todo={todo} markComplete={this.props.markComplete} delTodo={this.props.delTodo}/>
));
}
}
TodoItem.js
render() {
const { id, title } = this.props.todo;
return (
<div style={this.getStyle()}>
<p>
<input type="checkbox" onChange={this.props.markComplete.bind(this, id)} />{' '}
{ title }
<button className="delete" style={btnStyle} onClick={this.props.delTodo.bind(this,id)}>x</button>
</p>
</div>
)
}
}
No TodoItems show up in the UI as stated before.
Error message:
Warning: Failed prop type: The prop todos is marked as required in TodoItem, but its value is undefined.
in TodoItem (at Todos.js:8)
in Todos (at App.js:87)
in Route (at App.js:81)
in div (at App.js:79)
in div (at App.js:78)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:77)
in App (at src/index.js:4)
Error I get when I try to add a new TodoItem:
AddTodo error
Error: Reference.set failed: First argument contains undefined in property 'todos.7cda085d-7653-4895-a140-d6f2629af9ca.id'
C:/Users/Paul/Desktop/react-todo/src/App.js:70
67 | title: title,
68 | completed: false
69 | }
> 70 | todosRef.set(newTodo).then(
^ 71 | this.setState({todos: [...this.state.todos, newTodo]})
72 | );
73 | };
262 |
263 | this.onSubmit = e => {
264 | e.preventDefault();
> 265 | this.props.addTodo(this.state.title);
266 | this.setState({
267 | title: ''
268 | });
onClick={this.props.delTodo.bind(this,id)}
You don't need to bind this here.
You just need to create an anonymous function and call your markComplete and delTodo function with given id in that anonymous function,
render() {
const { id, title } = this.props.todo;
return (
<div style={this.getStyle()}>
<p>
<input type="checkbox" onChange={() => this.props.markComplete(id)} />{' '}
{ title }
<button className="delete" style={btnStyle} onClick={() => this.props.delTodo(id)}>x</button>
</p>
</div>
)
}
}
Related
I have built this modal component using react hooks. However the data that the modal shows when it pops up its incorrect (it always shows the name property for last element in the array).
//Modal.js
import ReactDOM from 'react-dom';
const Modal = ({ isShowing, hide, home_team }) => {return isShowing ? ReactDOM.createPortal(
<React.Fragment>
<div className="modal-overlay"/>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-header">
<a>Home team: {home_team}</a>
<button type="button" className="modal-close-button" onClick={hide}>
</button>
</div>
</div>
</div>
</React.Fragment>, document.body
) : null;}
export default Modal;
// Main component
const League = ({ league, matches }) =>{
const {isShowing, toggle} = useModal();
return (
<Fragment>
<h2>{league}</h2>
{
matches.map((
{
match_id,
country_id,
home_team
}
) =>
{
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
})
}
</Fragment>
)};
This is what matches data set looks like:
[{
match_id: "269568",
country_id:"22",
home_team: "Real Kings"
},
{
match_id: "269569",
country_id:"22",
home_team: "Steenberg United"
},
{
match_id: "269570",
country_id:"22",
home_team: "JDR Stars "
},
{
match_id: "269571",
country_id:"22",
home_team: "Pretoria U"
},
]
I am not sure whats going on because the data seems to be passed fine.
<p>{home_team}</p>
in the main component is showing everytime the expected property, however the Modal always shows the last home_team item in the array (e.g.Pretoria U)
you need to call useModal inside of the map function. otherwise you will open on toggle all Modals and the last one overlaps the others
const HomeTeam = ({ match_id, country_id, home_team }) => {
const {isShowing, toggle} = useModal();
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
}
const League = ({ league, matches }) => (
<Fragment>
<h2>{league}</h2>
{ matches.map((match) => <Hometeam {...match} /> }
</Fragment>
);
A working example of my problem can be found at:
https://codepen.io/RyanCRickert/pen/vYYQeaW
I am prop drilling a function two levels and passing that function along with an index to a rendered component. When a name is submitted it renders a new component which shows the name and div which has an onClick (X). I am trying to receive the index of where the name is located in the array which it lives so that I may splice it out when the button is clicked.
If I enter the name "Bob" for example, then click the div with the listener I can console log the event.target. Using the above example I get "<div class='person-item__X' value='0'>X</div>" for event.target and undefined for event.target.value. The value is being assigned as <div onClick={props.removeName} class="person-item__X" value={props.value}>X</div>.
Am I just unable to grab the value of a div in such a manor? Or is there something that I am missing? Thank you
Change these to your code
const PersonListItem = props => (
<div class="person-item">
<div class="person-item__name">{props.name}</div>
<div onClick={() => props.removeName(props.value)} class="person-item__X" value={props.value}>X</div>
</div>
);
Inside PeopleList replace this line
<PersonListItem key={index} name={person} value={index} removeName={(id) => props.removeName(id)} />
Inside TeamGenerator replace this line
<PeopleList people={this.state.names} removeName={(id) => this.handleRemoveName(id)} />
now in handleRemoveName you will recieve a id of the item on which X was clicked
handleRemoveName = id => {
const currentArr = this.state.names;
console.log(id);
}
In your case, to grab the value inside this div, you should use ref API.
Your code should look like this:
TeamGenerator.js
import React, { Component } from "react";
import CustomModal from "./Modal";
import PeopleList from "./PeopleList";
import "./index.css";
export default class App extends Component {
constructor(props) {
super(props);
// Create a ref
this.divTextRef = React.createRef();
this.state = {
names: [],
selectedName: ""
};
}
handleCloseModal = () => {
this.setState({
selectedName: ""
});
};
handleChange = e => {
this.setState({ name: e.target.value });
};
handleRemoveName = index => {
// Get your name and index this way
console.log("Your text: ", this.divTextRef.current.innerHTML);
console.log("Your index: ", index);
};
handleSubmit = e => {
e.preventDefault();
const currentNames = this.state.names;
if (this.state.name)
currentNames.push(
this.state.name[0].toUpperCase() + this.state.name.slice(1)
);
this.setState({
name: "",
names: currentNames
});
};
render() {
return (
<div className="container">
<CustomModal
selectedName={this.state.selectedName}
closeModal={this.handleCloseModal}
/>
<form onSubmit={this.handleSubmit}>
<label>
Add name:
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
<div className="people-list-container">
<PeopleList
people={this.state.names}
removeName={this.handleRemoveName}
upperRef={this.divTextRef} // Pass the ref down from your Component tree
/>
</div>
</div>
);
}
}
PeopleList.js
import React from "react";
import PersonListItem from "./PersonListItem";
export default class PeopleList extends React.Component {
render() {
return (
<div className="people-container">
<div className="people-title">List of people</div>
<div className="people-list">
{this.props.people.length === 0 ? (
<div className="people-item">
<span>No people added</span>
</div>
) : (
this.props.people.map((person, index) => (
<PersonListItem
key={index}
name={person}
value={index}
removeName={() => this.props.removeName(index)} // Passing index to the removeName function of Parent
upperRef={this.props.upperRef} // Continue passing it down to PersonListItem
/>
))
)}
</div>
</div>
);
}
}
PersonListItem.js
import React from "react";
const PersonListItem = props => (
<div className="person-item">
<div ref={props.upperRef} className="person-item__name"> // Use the passed ref
{props.name}
</div>
<div
onClick={props.removeName}
className="person-item__X"
value={props.value}
>
X
</div>
</div>
);
export default PersonListItem;
The div node does not have the value like input, so you can not grab it by your old way.
Hello friends! I hope you are well.
I've got an arrow function called WorldInfo and its parent component is passing down an object in props that for the sake of this example, I'm just calling object. Now In WorldInfo I also want to parse and list the items in object, so I've created the method serverInfoTabList to take object and shove it through .map. My problem is when compiled, my browser does not recognize serverInfoTabList either when it's defined nor called in WorldInfo's own return function.
Here is the error and the code itself.
Line 7:5: 'serverInfoTabList' is not defined no-undef
Line 34:22: 'serverInfoTabList' is not defined no-undef
const WorldInfo = (props) => {
serverInfoTabList = (object) => {
if (object != undefined){
return object.item.map((item) => {
const time = Math.trunc(item.time/60)
return (
<li key={item._id}>{item.name}
<br/>
Minutes Online: {time}
</li>
);
});
}
}
return (
props.object!= undefined ?
<div className={props.className}>
<h1>{props.world.map}</h1>
{/* <img src={props.object.image}/> */}
<div>
<ul>
{serverInfoTabList(props.object)}
</ul>
</div>
</div>
:
null
);
}
Thanks for your time friendos!
You forgot the const declaration
const serverInfoTabList = (object) => {
/* ... */
}
The other problem is that you're accessing properties which doesn't exist props.world for instance. Also you're mapping through an undefined property props.object.item. I've corrected your sandbox
const WorldInfo = props => {
const serverInfoTabList = object => {
return Object.keys(object).map(key => {
const item = object[key];
const time = Math.trunc(item.time / 60);
return (
<li key={item._id}>
{item.name}
<br />
Minutes Online: {time}
</li>
);
});
};
return props.object ? (
<div className={props.className}>
<h1>{props.world.map}</h1>
{/* <img src={props.object.image}/> */}
<div>
<ul>{serverInfoTabList(props.object)}</ul>
</div>
</div>
) : null;
};
class Todo extends Component {
render() {
const object = { item1: { _id: 1, time: 1 }, Item2: { _id: 2, time: 2 } };
return (
<div>
<WorldInfo object={object} world={{ map: "foo" }} />
</div>
);
}
}
this is my code
please anybody help me to solve this problem
import React, { Component } from 'react'
import Grid from '#material-ui/core/Grid';
import axios from 'axios';
class home extends Component {
state = {
screams:null
}
//screams: [] also not working
componentDidMount(){
axios.get('/screams')
.then(res =>{
this.setState({
screams: res.data
})
})
.catch(err =>{
console.log(err)
})
}
render() {
let recentScreams = this.state.screams ? (
this.state.screams.map(scream => <p>{scream.body}</p>)
) : <p>loading ....</p>
return (
<Grid container spacing={16}>
<Grid item sm={8} xs={12}>
{recentScreams}
</Grid>
<Grid item sm={4} xs={6}>
<p>profile ....</p>
</Grid>
</Grid>
)
}
}
export default home;
error is
TypeError: this.state.screams.map is not a function
19 | }
20 |
21 | render() {
> 22 | let recentScreams = this.state.screams ? (
| ^ 23 | this.state.screams.map(scream => <p>{scream.body}</p>)
24 | ) : <p>loading ....</p>
25 | return (
i refer this question answer before post this questions here
1.React JS - Uncaught TypeError: this.props.data.map is not a function
2.TypeError: this.state.patients.map is not a function
3.ReactJS: TypeError: this.state.data.map is not a function
4.Uncaught TypeError: s.map is not a function in ReactJs
5.React .map is not a function
this is my api/screams results
{
"scream": [
{
"screamId": "1WRwEClETpvWRyPADUKh",
"body": "hello all welcome to my site",
"userHandle": "image",
"createAt": "2019-07-25T05:39:52.099Z",
"userImage": "https://firebasestorage.googleapis.com/v0/b/demoapp-c1dab.appspot.com/o/no-img-test.png?alt=media"
},
{
"screamId": "csFCMA4wY3NXuKpZAVr8",
"body": "oops everything working pass 7",
"userHandle": "newapi",
"createAt": "2019-07-22T07:21:32.964Z"
},
{
"screamId": "Kit3XcAEFu8DWOE4IoXJ",
"body": "hello world from no token",
"userHandle": "Ghost",
"createAt": "2019-07-21T17:00:00.091Z"
}
]
}
Your variable will be set to null before fetching the data, leading to your error on the first rendering.
Try setting your variable to an empty array first :
state = {
screams: []
}
Just saw the data you fetched. I think you need to get the array that is into your JSON response :
this.setState({
screams: res.data.scream
})
Instead of :
this.setState({
screams: res.data
})
I'm learning React and I'm trying to render the <Comment/> component inside of it self, however I get the following error:
TypeError: Cannot read property 'map' of undefined
Comment._this.getResponses
src/Comment.js:28
25 | );
26 | }
27 | getResponses = () => {
> 28 | return this.props.responses.map(p => {
| ^ 29 | return (
30 | <Comment
31 | author={p.author}
and the code:
import React, { Component } from "react";
class Comment extends Component {
render() {
return (
<div className="comment">
<a className="avatar">
<img src={this.props.avatar} />
</a>
<div className="content">
<a className="author">{this.props.author}</a>
<div className="metadata">
<span className="date">{this.props.timeStamp}</span>
</div>
<div className="text">
<p>{this.props.text}</p>
</div>
<div className="actions">
<a className="reply">Reply</a>
</div>
</div>
<div className="comments">{this.getResponses()}</div>
</div>
);
}
getResponses = () => {
return this.props.responses.map(p => {
return (
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
);
});
};
}
export default Comment;
Please note that this.props.responses is not undefined, and the problem only occurs while I'm trying to use the Comment component. If I replace the Comment component here:
return this.props.responses.map(p => {
return <Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
});
with something like this:
return this.props.responses.map(p => {
return (
<div>
<h1>author={p.author}</h1>
<h1>avatar={p.avatar}</h1>
<h1>timeStamp={p.timeStamp}</h1>
<h1>text={p.text}</h1>
</div>
);
});
the code works.
This is because the rendering of <Comment /> relies on the responses prop being defined.
Currently, when you render Comment components in getResponses(), there is no responses prop assigned to those comments:
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
This in turn means an error will be thrown when these <Comment /> components are rendered, and they attempt to render "children" of their own (during the call to getResponses()) via the undefined responses prop.
To resolve this, you can check to see that the this.props.responses array is defined before proceeding to map and render <Comment/> components in the getResponses() method, like so:
getResponses = () => {
// Check that responses prop is an array before
// attempting to render child Comment components
if(!Array.isArray(this.props.responses)) {
return null;
}
return this.props.responses.map(p => {
return (
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
);
});
};