I am building a React-Firebase app, and the whole app was running smoothly. The authentication, the creating, logging in and logging out of the app was all very fast. Once I started to create links to the firestore, it became very slow. I have used Firebase before in instant messaging apps and it was very fast, but for some reason, it isn't anymore.
Here is the file that is causing the app to be slow:
import React, {useEffect, useState} from 'react'
import firebase from 'firebase/compat'
import { db } from '../firebase.js'
import Navbar from './Navbar'
import './styles/MasterScreen.css'
const MasterScreen = ({typeOfUser}) => {
const [customer, setCustomers] = useState([])
useEffect(() => {
async function fetchData() {
await firebase.firestore().collection('users').onSnapshot(snapshot => {
setCustomers(snapshot.docs.map(doc => doc.data()).filter(doc => doc.typeOfUser === 'customer'))
})
}
fetchData()
})
async function changeState(uid, state) {
// await db.collection('users').onSnapshot(snapshot => {
// snapshot.docs.map(doc => doc.data().uid === uid && db.collection('users').doc(doc.id).update({state}))
// })
const snapshot = await firebase.firestore().collection('users').get()
snapshot.docs.map(doc => doc.data().uid === uid && firebase.firestore().collection('users').doc(doc.id).update({state}))
}
return (
<div>
<Navbar typeOfUser={typeOfUser} />
<div className="customers">
{customers.map(e => (
<div key={e.uid} className="customer">
<div className={`state ${e.state === 'go' ? 'green' : e.state === 'slow' ? 'yellow' : e.state === 'stop' ? 'red' : 'pause'}`}>
<h3>CURRENT STATE</h3>
</div>
<div className="customerContents">
<button className='goOne btn' onClick={() => changeState(e.uid, 'go')}>GO</button>
<button className='slowOne btn' onClick={() => changeState(e.uid, 'slow')}>SLOW</button>
<button className='stopOne btn' onClick={() => changeState(e.uid, 'stop')}>STOP</button>
<button className='pauseOne btn' onClick={() => changeState(e.uid, 'pause')}>PAUSE</button>
</div>
</div>
))}
</div>
</div>
)
}
export default MasterScreen
My logic is to go inside each document in the collection and find the doc that has the same uid as the one given. Then upload the 'state' of the user to Firebase. I tried removing onSnapshot to make it faster.
If you could help, that would be create!
Related
I'm trying to update a review then send it to my database but i get no response or error in the console, the delete is working well and deleting on the database.
'''
import React, { useState, useEffect } from 'react'
import axios from 'axios'
function Renderreview() {
const [renderReview, setRenderReview] = useState([])
useEffect(() => {
axios.get('/reviews')
.then(res => {
console.log(res)
setRenderReview(res.data)
})
.catch(err => {
console.log(err)
})
}, [])
function handleDelete(id) {
axios.delete(`/reviews/${id}`,)
}
function handleSubmit(id){
axios.update(`/reviews/${id}`,)
}
return (
<div className='card1'>
<h2>reviews</h2>
{renderReview.map((renderReview) => {
return (
<div className='renderedreviews'>{renderReview.review}
<button
onClick={() => {
handleDelete(renderReview.id);
}}
key={renderReview.review}>
Delete
</button>
<form className='reviews' onSubmit={(e) => handleSubmit(e)}>
<input onChange={(e)=>handleSubmit(e)} id="review" defaultvalue={renderReview.review} placeholder="edit" type="text"></input>
<button onClick={() =>{
handleSubmit(renderReview.id);
}}
key ={renderReview.review}
>edit review</button>
</form>
</div>
)
})}
</div>
)
}
export default Renderreview
'''
Maybe you intended to use axios.put instead of axios.update, as PUT is the method usually used to send update requests
I have a problem and I need you to help me understand it. I am using ReactJS and I am building a simple CRUD Todo App. I Want to store my todos in local storage.
The data is saved there and I can see it but after the refresh it is emptying my local storage.
What am I doing wrong?
Something that I notice is that from the first time when I open the app (first rendering), local storage is creating the storage space without adding a todo.
Could I have missed something in my code that makes it reset it or empty it when the page is rendered?
import React, { useState, useEffect } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faCheck,
faPen,
faPlus,
faTrashCan,
} from "#fortawesome/free-solid-svg-icons";
import "./App.css";
import { faCircleCheck } from "#fortawesome/free-regular-svg-icons";
function App() {
const [todos, setTodos] = useState([]);
const [todo, setTodo] = useState("");
const [todoEditing, setTodoEditing] = useState(null);
const [editingText, setEditingText] = useState("");
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
setTodos(loadedTodos);
}
}, []);
useEffect(() => {
const json = JSON.stringify(todos);
window.localStorage.setItem("todos", json);
}, [todos]);
function handleSubmit(e) {
e.preventDefault();
const newTodo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setTodos([...todos].concat(newTodo));
setTodo("");
}
function deleteTodo(id) {
const updatedTodos = [...todos].filter((todo) => todo.id !== id);
setTodos(updatedTodos);
}
function toggleComplete(id) {
let updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setTodos(updatedTodos);
}
function submitEdits(id) {
const updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.text = editingText;
}
return todo;
});
setTodos(updatedTodos);
setTodoEditing(null);
}
return (
<div className="App">
<div className="app-container">
<div className="todo-header">
<form onSubmit={handleSubmit}>
<input
type="text"
name="todo-input-text"
placeholder="write a todo..."
onChange={(e) => {
setTodo(e.target.value);
}}
value={todo}
/>
<button>
<FontAwesomeIcon icon={faPlus} />
</button>
</form>
</div>
<div className="todo-body">
{todos.map((todo) => {
return (
<div className="todo-wrapper" key={todo.id}>
{todo.id === todoEditing ? (
<input
className="edited-todo"
type="text"
onChange={(e) => setEditingText(e.target.value)}
/>
) : (
<p className={todo.completed ? "completed" : "uncompleted"}>
{todo.text}
</p>
)}
<div className="todo-buttons-wrapper">
<button onClick={() => toggleComplete(todo.id)}>
<FontAwesomeIcon icon={faCircleCheck} />
</button>
{todo.id === todoEditing ? (
<button onClick={() => submitEdits(todo.id)}>
<FontAwesomeIcon icon={faCheck} />
</button>
) : (
<button onClick={() => setTodoEditing(todo.id)}>
<FontAwesomeIcon icon={faPen} />
</button>
)}
<button
onClick={() => {
deleteTodo(todo.id);
}}
>
<FontAwesomeIcon icon={faTrashCan} />
</button>
</div>
</div>
);
})}
</div>
</div>
</div>
);
}
export default App;
You should be loading todos from localStorage on the Component mount if they are available in localStorage like this,
const loadedTodos = localStorage.getItem("todos")
? JSON.parse(localStorage.getItem("todos"))
: []; // new
const [todos, setTodos] = useState(loadedTodos); // updated
And then you don't have to mutate the state using setTodos(loadedTodos) in the useEffect.
Just remove this useEffect , from the code:
// that useEffect should be removed
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
setTodos(loadedTodos);
}
}, []);
You can check this in the working CodeSandbox as well.
I think your second useEffect is causing it to reset.
Move that the useEffect logic to a separate function.
And instead of calling setTodos, call that function, update the storage, and then call setTodos from that function.
If you call the setTodos function with a callback function and spread operator like this it should work:
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
// set local storage like this
setTodos( prevTodos => [...prevTodos, ...loadedTodos] );
}}, []);
I have been following a tutorial to make a MERN e-commerce website:
Youtube Video Tutorial: VIDEO
Github containing all the code from the creator of the video: Github
And since it was published at December 2020, I encounter some deprecated/obsolete code that used to work for the creator of the video but doesn't work anymore.
I managed to solve most of the issues but at 3:31:26 of the video, he gets a bug and fixes it easily. The problem is that his solution doesn't make sense and it doesn't work. I have a different error but the error is logically generated. If you look at the video you will notice he does "cartItems.map" but when console logging cartItems it gives me that it is an object. I've tried replacing my code with his, but nothing. Replacing with his Github's code. Neither.
The error I receive when I click on "add to cart" from the product's page
This is where the web browser points to in the code, which is where cartItems is used as an array but it isn't (I also tried [...Array(cartItems)].map and didn't work
CartScreen.jsx
import './CartScreen.css'
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from "react-router-dom";
// Components
import CartItem from "../components/CartItem";
const CartScreen = () => {
const dispatch = useDispatch();
const cart = useSelector(state => state.cart);
const { cartItems } = cart;
return (
<div className='cartscreen'>
<div className='cartscreen__left'>
<h2>Shopping Cart</h2>
{console.log(typeof cartItems)}
{cartItems.length === 0 ? (
<div>
Your Cart is empty <Link to="/">Go Back</Link>
</div>
) : (
cartItems.map((item) =>
<CartItem item={item} />
))}
</div>
<div className='cartscreen__right'>
<div className='cartscreen__info'>
<p>Subtotal (0) items</p>
<p>$499.99</p>
<div>
<button>Proceed to Checkout</button>
</div>
</div>
</div>
</div>
)
}
export default CartScreen;
CartItem.jsx
import "./CartItem.css";
import { Link } from "react-router-dom";
const CartItem = ({ item, qtyChangeHandler, removeHandler }) => {
return (
<div className="cartitem">
<div className="cartitem__image">
<img src={item.imageUrl} alt={item.name} />
</div>
<Link to={`/product/${item.product}`} className="cartItem__name">
<p>{item.name}</p>
</Link>
<p className="cartitem__price">${item.price}</p>
<select
value={item.qty}
onChange={(e) => qtyChangeHandler(item.product, e.target.value)}
className="cartItem__select"
>
{[...Array(item.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</select>
<button
className="cartItem__deleteBtn"
onClick={() => removeHandler(item.product)}
>
<i className="fas fa-trash"></i>
</button>
</div>
);
};
export default CartItem;
cartActions.jsx
import * as actionTypes from '../constants/cartConstants';
import axios from 'axios';
export const addToCart = (id, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/products/${id}`);
dispatch({
type: actionTypes.ADD_TO_CART,
payload: {
product: data.id,
name: data.new,
imageURL: data.imageUrl,
price: data.price,
countInStock: data.countInStock,
qty
},
});
localStorage.setItem('cart', JSON.stringify(getState().cart.cartItems));
};
export const removeFromCart = (id) => (dispatch, getState) =>{
dispatch({
type: actionTypes.REMOVE_FROM_CART,
payload: id
});
localStorage.setItem('cart', JSON.stringify(getState().cart.cartItems));
};
ProductScreen.jsx
import "./ProductScreen.css";
import { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useParams, useNavigate } from "react-router-dom";
// Actions
import { getProductDetails } from "../redux/actions/productActions";
import { addToCart } from "../redux/actions/cartActions";
const ProductScreen = ({ history }) => {
const [qty, setQty] = useState(1);
const dispatch = useDispatch();
const productDetails = useSelector((state) => state.getProductDetails);
const { loading, error, product } = productDetails;
const { id } = useParams();
const navigate = useNavigate();
useEffect(() => {
if (product && id !== product._id) {
dispatch(getProductDetails(id));
}
}, [dispatch, product]);
const addToCartHandler = () => {
dispatch(addToCart(product._id, qty));
navigate("/cart");
};
return (
<div className="productscreen">
{loading ? (
<h2>Loading...</h2>
) : error ? (
<h2>{error}</h2>
) : (
<>
<div className="productscreen__left">
<div className="left__image">
<img src={product.imageUrl} alt={product.name} />
</div>
<div className="left__info">
<p className="left__name">{product.name}</p>
<p>Price: ${product.price}</p>
<p>Description: {product.description}</p>
</div>
</div>
<div className="productscreen__right">
<div className="right__info">
<p>
Price:
<span>${product.price}</span>
</p>
<p>
Status:
<span>
{product.countInStock > 0 ? "In Stock" : "Out of Stock"}
</span>
</p>
<p>
Qty
<select value={qty} onChange={(e) => setQty(e.target.value)}>
{[...Array(product.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</select>
</p>
<p>
<button type="button" onClick={addToCartHandler}>
Add To Cart
</button>
</p>
</div>
</div>
</>
)}
</div>
);
};
export default ProductScreen;
Nothing here is an obsolete style that "does not work any more" - if something doesn't work, it's not because of something being discontinued.
In this explicit case you should probably write something like
// change this
{cartItems.length === 0 ? (
// to this
{!cartItems || cartItems.length === 0 ? (
But generally, you are right, this is an outdated style of Redux and you will probably end up writing multiple times the amount of code than you would need to write if you were writing modern code. Modern Redux does not use switch..case reducers, ACTION_TYPES, you will not dispatch actions as objects directly etc. etc.
Please do yourself a favor and follow the official Redux tutorial instead and also look for other sources that teach up-to-date knowledge.
I am practicing REST API by using one Fake API site. For front-end, I am using React. I successfully login the email and password by using Fake API's login and redirect to list users, where I fetched the data from Fake API and shows the user's name, image. I used the delete button where I can delete the user. I used the Axios delete method but the button does not do anything.
This is the component
import React, { useState, useEffect } from "react";
import axios from "axios";
import { NavLink } from "react-router-dom";
function Users() {
const [state, setState] = useState([]);
useEffect(() => {
fetchingData();
}, []);
const removeData = id => { //THIS IS WHERE I USED THE AXIOS DELETE METHOD
axios
.delete(`https://reqres.in/api/users/${id}`)
.then(res => console.log(res))
.catch(err => console.log(err));
};
const fetchingData = () => {
axios
.get("https://reqres.in/api/users")
.then(response => {
setState(response.data.data);
})
.catch(err => console.log(err));
};
return (
<div>
<div className="col s12 m7">
<h2 className="header">Users</h2>
{state.map(userlist => {
return (
<div className="card horizontal" key={userlist.id}>
<div className="card-image">
<img src={userlist.avatar} alt="images" />
</div>
<div className="card-stacked">
<div className="card-content">
<p>
Name: {userlist.first_name} {userlist.last_name}
</p>
<p>Email: {userlist.email}</p>
</div>
<div className="card-action">
<button
className="btn delete"
onClick={() => removeData !== userlist.id} // THIS IS WHERE I USED THE DELETE LOGIC.
>
Delete
</button>
<NavLink
exact
to={`/api/users/${userlist.id}`}
className="btn edit"
>
Edit
</NavLink>
</div>
</div>
</div>
);
})}
</div>
</div>
);
}
export default Users;
In your delete button's onClick prop, you should pass the id as an argument.
const removeData = async id => {
console.log("ID", id);
try {
const response = await axios.delete(`https://reqres.in/api/users/${id}`);
console.log(response);
fetchingData();
} catch (error) {
console.log(error);
}
};
<button
className="btn delete"
onClick={() => removeData(userlist.id)} // THIS IS WHERE I USED THE DELETE LOGIC.
>
Delete
</button>;
The following code shows a list of 10 users (list-view) and if you click on Details button of any of those users, it shows only that particular user (user-view).
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me/?results=10'
)
console.log(response.data.results)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
return (
<ul className='card__wrapper'>
{resources.filter(user => (id) ? user.login.uuid === id : true)
.map(item => (
<li className='card' key={item.name.first}>
<div className='card__item'>
<img className='card__image' src={item.picture.large} alt={item.name.first} />
<h2 className='card__title'>{item.name.first} {item.name.last}</h2>
{
id
?
<button
className='card__cta'
onClick={() => setID(null)}
>
Back to overview
</button>
:
<button
className='card__cta'
onClick={() => setID(item.login.uuid)}
>
Details
</button>
}
</div>
</li>
))}
</ul>
)
}
export default UserList
While this is working fine, the code inside the return which builds both the list-view and also the user-view is a bit difficult to understand (at least for me) and also makes it hard for using different CSS classes for List- and User-view.
I'd like to simplify the code so that's easier to understand by splitting it to two different returns.
Basically, saying that if the condition is true, return the user-view otherwise the list-view
How can I do that?
I would put the rendering stuff into another function, and to make what is going to be clearer I would use two returns:
import React, { useState, useEffect } from "react";
import axios from "axios";
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([]);
const fetchResource = async () => {
const response = await axios.get("https://api.randomuser.me/?results=10");
console.log(response.data.results);
setResources(response.data.results);
};
useEffect(() => {
fetchResource();
}, []);
const renderItem = (item, isLoggedIn) => {
return (
<li className="card" key={item.name.first}>
<div className="card__item">
<img className="card__image" src={item.picture.large} alt={item.name.first} />
<h2 className="card__title">
{item.name.first} {item.name.last}
</h2>
{isLoggedIn ? (
<button className="card__cta" onClick={() => setID(null)}>
Back to overview
</button>
) : (
<button className="card__cta" onClick={() => setID(item.login.uuid)}>
Details
</button>
)}
</div>
</li>
);
};
const user = resources.find(user => user.login.uuid === id);
if (user) {
return <ul className="card__wrapper">{renderItem(user, true)}</ul>;
} else {
return <ul className="card__wrapper">{resources.map(user => renderItem(user, false))}</ul>;
}
};
export default UserList;
Looks like the question asked pertains to this React hooks - OnClick show only the clicked item
Please find my comment for the above post, as I guess this particular issue can be solved as mentioned in the comment! In case it doesn't fix, let me know.