I am creating a Todo List using React and Firebase. At the moment, I am able to add a todo, and have it render in the UI, which is a list that I import and map through. The problem, is that when I try and add an additional input, I am not able to render the second input, and only the first input gets rendered. Taking this into consideration I decided to console.log the two inputs, and I saw that the data was available in the console. I then previously created a question on Stackoverflow, thinking that my problem was the props I was expecting in the child component. But now, I think my issue lies with how my data is being passed down, to be mapped and then rendered. For example, my todo is being passed as an argument, but I also need my title to be passed as an argument when I map through my list elemnt. So my question is more about how can I get both pieces of data to render in my UI.
This is the parent component. AddLink.js.
import { useState, useEffect } from "react";
import classes from "./addlink.module.css";
import firebase from "firebase/app";
import initFirebase from "../../config";
import "firebase/firestore";
import Todo from "../Todo/Todo";
import { v4 as uuidv4 } from "uuid";
initFirebase();
const db = firebase.firestore();
function AddLink(props) {
const [todos, setTodos] = useState([]);
const [inputLinks, setInputLinks] = useState("");
const [inputHeaders, setInputHeaders] = useState("");
useEffect(() => {
db.collection("links")
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
setTodos(
snapshot.docs.map((doc) => ({
id: doc.id,
todo: doc.data().todo,
}))
);
});
}, []);
const addTodo = (event) => {
event.preventDefault();
db.collection("links").add({
id: uuidv4(),
todo: inputLinks,
title: inputHeaders,
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
console.log(inputLinks);
console.log(inputHeaders);
setInputLinks("");
setInputHeaders("");
};
return (
<div className={classes.addlink}>
<form>
<div className={classes.adminlink}>
<input
type="text"
value={inputLinks}
onChange={(event) => setInputLinks(event.target.value)}
/>
<input
type="text"
value={inputHeaders}
onChange={(event) => setInputHeaders(event.target.value)}
/>
<button
className={classes.adminbutton}
type="submit"
onClick={addTodo}
>
Add new link
</button>
</div>
</form>
{todos.map((todo, title) => (
<Todo id={todo.id} todo={todo} title={todo.title} key={todo.id} />
))}
</div>
);
}
export default AddLink;
And the child component Todo.js.
import React from "react";
import { AiOutlinePicture } from "react-icons/ai";
import { AiOutlineStar } from "react-icons/ai";
import { GoGraph } from "react-icons/go";
import DeleteForeverIcon from "@material-ui/icons/DeleteForever";
import classes from "./todo.module.css";
import firebase from "firebase/app";
import initFirebase from "../../config";
import "firebase/firestore";
initFirebase();
const db = firebase.firestore();
function Todo(props) {
const deleteHandler = (event) => {
db.collection("links").doc(props.todo.id).delete();
};
return (
<li className={classes.adminsection}>
<div className={classes.linkCards}>
<h3>{props.todo.title}</h3>
<p>{props.todo.todo}</p>
<div>
<AiOutlinePicture />
<AiOutlineStar />
<GoGraph />
<DeleteForeverIcon onClick={deleteHandler} />
</div>
</div>
</li>
);
}
export default Todo;
Any help would be greatly appreciated.
I think i saw that code also once before. You are adding the data in a wrong way and also reading it from the map the wrong way. Here is an example how it should work:
import { useState, useEffect } from "react";
import classes from "./addlink.module.css";
import firebase from "firebase/app";
import initFirebase from "../../config";
import "firebase/firestore";
import Todo from "../Todo/Todo";
import { v4 as uuidv4 } from "uuid";
initFirebase();
const db = firebase.firestore();
function AddLink(props) {
const [todos, setTodos] = useState([]);
const [inputLinks, setInputLinks] = useState("");
const [inputHeaders, setInputHeaders] = useState("");
useEffect(() => {
db.collection("links")
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
setTodos(
snapshot.docs.map((doc) => ({
id: doc.id,
todo: doc.data(),
}))
);
});
}, []);
const addTodo = (event) => {
event.preventDefault();
db.collection("links").add({
id: uuidv4(),
todo: inputLinks,
title: inputHeaders,
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
console.log(inputLinks);
console.log(inputHeaders);
setInputLinks("");
setInputHeaders("");
};
return (
<div className={classes.addlink}>
<form>
<div className={classes.adminlink}>
<input
type="text"
value={inputLinks}
onChange={(event) => setInputLinks(event.target.value)}
/>
<input
type="text"
value={inputHeaders}
onChange={(event) => setInputHeaders(event.target.value)}
/>
<button
className={classes.adminbutton}
type="submit"
onClick={addTodo}
>
Add new link
</button>
</div>
</form>
{todos.map((i) => (
<Todo id={i.id} todo={i.todo} title={i.todo.title} key={i.id} />
))}
</div>
);
}
export default AddLink;
The mistakes:
todo
is taking just part of the data:snapshot.docs.map((doc) => ({
id: doc.id,
todo: doc.data().todo,
}))
{todos.map((todo, title) => (
<Todo id={todo.id} todo={todo} title={todo.title} key={todo.id} />
))}