I just started with React and this is my first project. I added a delete icon. I just want when press it a console log will show some text just for testing and knowing how the props are passing between components. The problem is this text is not showing in the console. Please if anyone can help with that, I would appreciate it.
I have user components, allUser component, home component which included in the app.js
User.js component
import "./User.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faTimes } from "#fortawesome/free-solid-svg-icons";
function User(props) {
return (
<div className="singleUser">
<div className="user">
<div>{props.id}</div>
<div>{props.name}</div>
<div>{props.phone}</div>
<div>{props.email}</div>
</div>
<div className="iconClose">
<FontAwesomeIcon icon={faTimes} onClick={() => props.onDelete} />
</div>
</div>
);
}
import User from "./user";
import { useState, useEffect } from "react";
function Allusers({ onDelete }) {
const [isLoading, setIsLoading] = useState(false);
const [actualData, setActualData] = useState([""]);
useEffect(() => {
setIsLoading(true);
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => {
// const finalUsers = [];
// for (const key in data) {
// const u = {
// id: key,
// ...data[key],
// finalUsers.push(u);
// }
setIsLoading(false);
setActualData(data);
});
}, []);
if (isLoading) {
return (
<section>
<p>Loading ... </p>
</section>
);
}
return actualData.map((singlUser) => {
for (const key in singlUser) {
// console.log(singlUser.phone);
return (
<div className="userCard" key={singlUser.id}>
<User
id={singlUser.id}
name={singlUser.name}
email={singlUser.email}
phone={singlUser.phone}
key={singlUser.id}
onDelete={onDelete}
/>
</div>
);
}
});
}
export default Allusers;
import Navagation from "../components/Navagation";
import Allusers from "../components/Allusers";
import Footer from "../components/Footer";
function Home() {
const deleteHandler = () => {
console.log("something");
};
return (
<section>
<Navagation />
<Allusers onDelete={deleteHandler} />
</section>
);
}
export default Home;
You aren't actually calling the function with () => props.onDelete in User.js-- it needs to be () => props.onDelete() (note the parens added after props.onDelete).
<FontAwesomeIcon icon={faTimes} onClick={() => props.onDelete} />
...should be:
<FontAwesomeIcon icon={faTimes} onClick={() => props.onDelete()} />
Related
I am finishing up a project where I want to use a small dropdown menu when I click on my settings icon. The problem is that for some reason it is not recognized when I click outside of that dropdown menu. I used a hook that worked in the same project with a different dropdown menu, but now it doesn't. Maybe because it is in a modal? I really don't know.
Here is the Repo of this Project: https://github.com/Clytax/fem-kanban
The Hook (I modified it a bit by excluding the elipsis icon so it doesnt reopen when Click on it to close it.)
import React from "react";
export const useOutsideClick = (callback, exclude) => {
const ref = React.useRef();
React.useEffect(() => {
const handleClick = (e) => {
if (
ref.current &&
!ref.current.contains(e.target) &&
!exclude.current.contains(e.target)
) {
callback();
}
};
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, [callback, exclude]);
return ref;
};
The Modal:
import React, { useRef, useState, useEffect } from "react";
import "./taskModal.scss";
import { ReactComponent as Elipsis } from "../../../assets/Icons/icon-vertical-ellipsis.svg";
import { ReactComponent as Close } from "../../../assets/Icons/icon-chevron-up.svg";
import { ReactComponent as Open } from "../../../assets/Icons/icon-chevron-down.svg";
import { useSelector, useDispatch } from "react-redux";
import modalSlice, {
closeViewTaskModal,
openEditTaskModal,
closeAllModals,
openDeleteTaskModal,
} from "../../../features/global/modalSlice";
import Backdrop from "../Backdrop/Backdrop";
import Subtask from "../../Task/Subtask";
import "../../Extra/DropdownSettings.scss";
import { useOutsideClick } from "../../../hooks/useOutsideClick";
import { motion } from "framer-motion";
import DropdownStatus from "../../Extra/DropdownStatus";
import DropdownSettings from "../../Extra/DropdownSettings";
import DropdownSettingsTask from "../../Extra/DropdownSettingsTask";
const ViewTaskModal = ({ handleClose }) => {
const [openSettings, setOpenSettings] = useState(false);
const dispatch = useDispatch();
const task = useSelector((state) => state.modal.viewTaskModal.task);
const handleCloseSettings = () => {
console.log("hi");
setOpenSettings(false);
};
const modal = useSelector((state) => state.modal);
const viewTaskModal = useSelector((state) => state.modal.viewTaskModal);
const elipsisRef = useRef(null);
const wrapperRef = useOutsideClick(handleCloseSettings, elipsisRef);
const getFinishedSubTasks = () => {
let finishedSubTasks = 0;
task.subTasks.forEach((subtask) => {
if (subtask.isDone) {
finishedSubTasks++;
}
});
return finishedSubTasks;
};
const closeModal = () => {
dispatch(closeViewTaskModal());
};
return (
<Backdrop onClick={closeModal} mobile={false}>
<motion.div
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
e.stopPropagation();
}}
className="view-task"
>
<div className="view-task__header | flex">
<h2 className="view-task__header__title">{task.name}</h2>
<div className="view-tastk__settings">
<div
className="view-task__header__icon"
style={{ cursor: "pointer" }}
ref={elipsisRef}
onClick={() => {
setOpenSettings(!openSettings);
}}
>
<Elipsis />
</div>
{openSettings && (
<div className="dropdown-settings__task" ref={wrapperRef}>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openEditTaskModal(task));
}}
>
Edit Task
</div>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openDeleteTaskModal(task));
}}
>
Delete Task
</div>
</div>
)}
</div>
</div>
<p className="view-task__description">{task.description}</p>
<div className="view-task__subtasks">
<p>
Subtasks ({getFinishedSubTasks()} of {task.subTasks.length})
</p>
<div className="view-task__subtasks__list">
{task.subTasks.map((subtask, index) => (
<Subtask
subtaskID={subtask.id}
boardID={task.boardID}
taskID={task.id}
columnID={task.columnID}
key={index}
/>
))}
</div>
</div>
<div className="view-task__status">
<p>Current Status</p>
<DropdownStatus click={handleCloseSettings} task={task} />
</div>
</motion.div>
</Backdrop>
);
};
export default ViewTaskModal;
I have compiled a project with context. Crud operations work here without problems. However, there is a problem that when you click on the delete button,the confirm modal opens, if I click OK the selected contact must be deleted for his id.
But I don't know how to pass it's contact id to modal for delete. Now when I click ok function it falls into the catch block.
import React from "react";
import { Modal, Space } from "antd";
import { ExclamationCircleOutlined } from "#ant-design/icons";
import DeleteIcon from "../assets/icons/DeleteIcon";
import { useContext } from "react";
import { GlobalContext } from "../context/GlobalState";
import { useParams } from "react-router-dom";
const ConfirmModal = () => {
const {id} = useParams()
const { REMOVE_CONTACT, contacts } = useContext(GlobalContext);
const confirm = () => {
Modal.confirm({
title: 'Do you want to delete this contact?',
icon: <ExclamationCircleOutlined />,
onOk() {
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
})
.then(() =>REMOVE_CONTACT(contacts.find((user) => user.id === id)))
.catch(()=>console.log('error'))
},
onCancel() {},
});
};
return (
<>
<div onClick={confirm}>
<DeleteIcon/>
</div>
</>
);
};
export default ConfirmModal;
import React, { useContext } from "react";
import { GlobalContext } from "../../context/GlobalState";
import DeleteIcon from "../../assets/icons/DeleteIcon";
import EditIcon from "../../assets/icons/EditIcon";
import styles from "../Contacts/contacts.module.scss";
import { NavLink } from "react-router-dom";
import InfoModal from "../../utils/InfoModal";
import ConfirmModal from "../../utils/ConfirmModal";
const Contacts = () => {
const { contacts, REMOVE_CONTACT } = useContext(GlobalContext);
return (
<div className={styles.contacts} >
<div className="container">
<div className="row d-flex justify-content-center">
{contacts.map((contact) => (
<div className="col-lg-7 p-3" key={contact.id}>
<div className={styles.contact}>
<div>
<h1 className={styles.title}>
{contact.name} {contact.surname} {contact.fatherName}
</h1>
<p className={styles.desc}>{contact.specialty}</p>
</div>
<div className={styles.btns}>
<div className={`${styles.contactBtn} ${styles.infoBtn}`}>
<InfoModal/>
</div>
<NavLink className={`${styles.contactBtn} ${styles.editBtn}`} to={`/contact/edit/${contact.id}`}>
<EditIcon contact={contact} />
</NavLink>
<div className={`${styles.contactBtn} ${styles.deleteBtn}`} >
<ConfirmModal />
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default Contacts;
enter image description here
You should accept an id prop in the ConfirmModal component, like this:
const ConfirmModal = ({id}) => {
const { REMOVE_CONTACT, contacts } = useContext(GlobalContext);
const confirm = () => {
Modal.confirm({
title: 'Do you want to delete this contact?',
icon: <ExclamationCircleOutlined />,
onOk() {
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
})
.then(() =>REMOVE_CONTACT(contacts.find((user) => user.id === id)))
.catch(()=>console.log('error'))
},
onCancel() {},
});
};
return (
<>
<div onClick={confirm}>
<DeleteIcon/>
</div>
</>
);
};
Also, pass the id where ConfirmModal is used, like this:
<ConfirmModal id={contact.id}/>
Also, change your onOk handler, to something more deterministic, using Math.random will get you a lot of errors:
onOk() {
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
})
.then(() =>REMOVE_CONTACT(contacts.find((user) => user.id === id)))
.catch(()=>console.log('error'))
}
If you console.log the error from the catch block, it'll tell you what's wrong.
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
})
.then(() =>REMOVE_CONTACT(contacts.find((user) => user.id === id)))
.catch((err) => console.log(err))
I am trying to get the id from a doc when I click a delete button. so the user can delete the post.
I got far enough to show the button if the user is the uploader.
but, I don't know how to get the id from the clicked document.
I tried this:
{auth.currentUser.uid === uploaderId ? (
<div
onClick={(e) => {
if (auth.currentUser.uid === uploaderId) {
e.stopPropagation();
deleteDoc(doc(db, "posts", id));
}
}}
>
<div>
<button>🗑️</button>
</div>
</div>
) : (
<div>
</div>
)}
but that doesn't work and gives me the error:
Uncaught TypeError: Cannot read properties of undefined (reading 'indexOf')
so to summarize how do I get the id from the document
full code:
import React, { useEffect, useRef, useState } from 'react';
import './App.css';
import { initializeApp } from "firebase/app";
import { getFirestore, collection, orderBy,
query, Timestamp, addDoc, limitToLast,
deleteDoc, doc, } from "firebase/firestore";
import { confirmPasswordReset, getAuth, GoogleAuthProvider, signInWithPopup, } from "firebase/auth";
import { useAuthState } from "react-firebase-hooks/auth";
import { useCollectionData } from "react-firebase-hooks/firestore";
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
function App() {
const [user] = useAuthState(auth);
return (
<div className="App">
<Header/>
{/* <Picker/> */}
<section>
<SignOut/>
{/* <ShowProfile/> */}
<Upload/>
{user ? <Feed /> : <SignIn />}
</section>
</div>
);
}
function Header() {
return (
<h1 className='headerTxt'>SHED</h1>
)
}
// function Picker() {
// }
function SignIn() {
const signInWithGoogle = () =>{
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
}
return (
<button className='SignIn' onClick={signInWithGoogle}>sign in with google</button>
)
}
function SignOut() {
return auth.currentUser && (
<button onClick={() => auth.signOut()}>Sign out</button>
)
}
function Feed() {
const postsRef = collection(db, 'posts');
const qwery = query(postsRef,orderBy('createdAt'), limitToLast(25), );
const [posts] = useCollectionData(qwery, {idField: "id"});
return (
<>
<main>
{posts && posts.map(msg => <Post key={msg.id} post={msg} />)}
</main>
</>
)
}
function Post(props) {
const {text, photoURL, displayName, uid, uploaderId, id} = props.post;
return (
<div className={"post"}>
<div className='msg'>
<div className='top'>
<img src={photoURL} alt="" />
<sub>{displayName}</sub>
</div>
<hr />
<p>{text}</p>
{auth.currentUser.uid === uploaderId ? (
<div
onClick={(e) => {
if (auth.currentUser.uid === uploaderId) {
e.stopPropagation();
deleteDoc(doc(db, "posts", id));
}
}}
>
<div>
<button>🗑️</button>
</div>
</div>
) : (
<div>
</div>
)}
{/* <button>❤️</button> */}
</div>
</div>
)
}
// function ShowProfile() {
// return(
// <div>
// <button onClick={queryMine}>my posts</button>
// </div>
// )
// }
function Upload() {
const postsRef = collection(db, 'posts');
const [formValue, setFormValue] = useState('');
const sendpost = async(e) =>{
e.preventDefault();
const {uid, photoURL, displayName} = auth.currentUser;
if (formValue !== "" && formValue !== " ") {
await addDoc(postsRef, {
text: formValue,
createdAt: Timestamp.now(),
displayName,
uploaderId: uid,
photoURL
})
setFormValue('')
}
}
return auth.currentUser &&(
<form onSubmit={sendpost}>
<textarea name="" id="" cols="30" rows="10"
value={formValue} onChange={(e) => setFormValue(e.target.value)}
></textarea>
<button type='submit'>➜</button>
</form>
)
}
export default App;```
Get the data (doc id and doc data) using querySnapshot and merge them into one object. Save an array of objects to a state and pass what you need as properties. For example:
...
const querySnapshot = await getDocs(collection(db, `users/${email}/todos`))
const todos = []
querySnapshot.forEach(doc => {
todos.push({
id: doc.id,
text: doc.data().text,
isCompleted: doc.data().isCompleted,
})
})
return todos
Here is a simple example of the Todo App, but the logic is the same. In the Todo component, I get the doc id as props. Full code:
import { collection, getDocs } from 'firebase/firestore'
import { useEffect, useState } from 'react'
import { db } from '../../app/firebase'
import Todo from '../Todo'
import './style.css'
const TodoList = () => {
const [todos, setTodos] = useState()
useEffect(() => {
const getData = async () => {
const querySnapshot = await getDocs(collection(db, 'users/kvv.prof#gmail.com/todos'))
const todos = []
querySnapshot.forEach(doc => {
todos.push({
id: doc.id,
text: doc.data().text,
isCompleted: doc.data().isCompleted,
})
})
return setTodos(todos)
}
getData()
}, [])
return (
<section className="todo-list">
{todos?.map(todo => (
<Todo key={todo.id} id={todo.id} text={todo.text} isCompleted={todo.isCompleted} />
))}
</section>
)
}
export default TodoList
If you use react-firebase-hooks, then you need to use this
I have json data file. I want to make a search with the names of the data and display the typed names when I click the search button.
I get the value of input in the console when I type something however I am not able to display it on the screen
How can I display the value of this input ?
my code is below
PostResults.jsx
import React from "react";
const PostResults = (props) => {
const {name} = props.posts
return(
<div className="">
<p className="titles">{name}</p>
</div>
)
}
export default PostResults
Posts.jsx
import React, { useState, useEffect } from "react";
import PostResults from './PostResults'
const Posts = (props) => {
const [posts, setPosts] = useState([]);
const [searchTerm,setSearchTerm]=useState([]);
const getData = () => {
fetch('data.json')
.then(response => {
return response.json()
//console.log(response)
})
.then(data => {
setPosts(data)
console.log(data)
})
}
useEffect(() => {
getData()
},[])
const submitHandler = (event) => {
event.preventDefault()
{searchTerm ? searchTerm : console.log("none")}
}
return(
<div className="">
<input
type="text"
placeholder="Search Anything"
name="query"
onChange={e => setSearchTerm(e.target.value)}
className="search-input"
/>
<button
onClick={submitHandler}
type="submit"
className="search-button"
>
<i className="fas fa-search"></i>
</button>
{posts.map(posts => (
<PostResults key={posts.id} posts={posts}/>
))}
</div>
)
}
export default Posts
App.jsx
import React from "react";
import "./style.css";
import 'bootstrap/dist/css/bootstrap.min.css'
import Posts from './components/Posts'
export default function App() {
return (
<div className="container">
<div className="row">
< Posts />
</div>
</div>
);
}
So I currently have this code that has a useEffect() that shows the data from the API every time. I refresh, but I'm trying to make that data display only when I click on my button. I'm not too sure where to go with my code.
import React, { useState, useEffect } from 'react';
import './App.css';
import axios from 'axios';
function App() {
const [image, setImage] = useState(false);
// I tried to put a onclick function, but not sure what to add here
const handleChange = ()
=> {
setImage(true)
}
// this code displays my data on refresh
useEffect(() => {
axios
.get(
'https://api.com'
)
.then(res => {
setImage ?
setImage(res.data.faces[0].urls[4][512]) : console.log('nothing')
})
.catch(err => {
console.log(err.message);
});
}, []);
return (
<div className='App'>
<h1>Photo Generator</h1>
<img src={image} />
<button onClick={handleChange}>Show new Image</button>
</div>
);
}
I've updated your code.
Try this code, let me know if it works for you. :)
import React, { useState } from "react";
import "./App.css";
import axios from "axios";
function App() {
const [image, setImage] = useState(false);
// I tried to put a onclick function, but not sure what to add here
const handleChange = () => {
axios
.get("https://api.com")
.then(res => {
const uri = res.data.faces[0].urls[4][512];
if (uri) {
setImage(uri);
} else {
console.log("nothing");
}
})
.catch(err => {
console.log(err.message);
});
};
return (
<div className="App">
<h1>Photo Generator</h1>
{image && <img src={image} alt="yourImage" />}
<button type="button" onClick={handleChange}>
Show new Image
</button>
</div>
);
}
You don't need to use useEffect Hook in this case.
also don't need to check setImage inside of API callback function.
You could do it like this
import React, {useState, useEffect} from 'react';
import './App.css';
import axios from 'axios';
function App() {
const [image, setImage] = useState('');
const [displayImage, setDisplayImage] = useState('none');
const handleChange = () => {
setDisplayImage('flex');
};
useEffect(() => {
axios
.get('https://api.com')
.then((res) => {
setImage
? setImage(res.data.faces[0].urls[4][512])
: console.log('nothing');
})
.catch((err) => {
console.log(err.message);
});
}, []);
return (
<div className="App">
<h1>Photo Generator</h1>
<div style={{display: displayImage}}>
<img style src={image} />
</div>
<button onClick={() => handleChange()}>Show new Image</button>
</div>
);
}
You could also do
<button onClick={() => setDisplayImage('flex')}>Show new Image</button>