Call Function out of React Component - javascript

I want to update a shopping cart when I click on a Product. But I don't know, how to call the function in another component.
This is, where the function is written and the state of the cart is hold.
export const Cart = () => {
const [userId, setUserId] = useState(7);
const [cart, setCart] = useState([]);
const [outfitName, setOutfitName] = useState("");
const sendOutfit = () => {
axios.post(`{url}/postOutfit`, {
userId,
outfitName,
cart,
});
};
function addToCart(id) {
cart.push(id);
setCart(cart);
}
...
}
Here I want to call the addToCart function.
import { Cart } from "../../sites/Cart/Cart";
...
<div className="product-name">
<button
className="button is-small is-outlined is-primary"
onClick={() =>
Cart.addToCart(product.id) & changeButtonText
}
>
{buttonText}
</button>
</div>
When I try to execute this, I get the following error message:
Do you have any suggestion for me?
Thank you very much!

You can not do this like that. Below I wrote simple example and here is nice article I suggest to read it first: Components and Props
const AddToCartButton = ({ setCart }) => {
return (
<button
onClick={() => {
setCart("item");
}}
></button>
);
};
const Cart = () => {
const [cart, setCart] = React.useState([]);
return <AddToCartButton setCart={setCart} />;
};

Methods in React return JSX values ​​and in this way it is not correct to export a method, if you want to export the method of addToCart to a another component you need to send this method as propeties or using state management for example as follows:
export const Cart = () => {
const [userId, setUserId] = useState(7);
const [cart, setCart] = useState([]);
const [outfitName, setOutfitName] = useState("");
const sendOutfit = () => {
axios.post(`{url}/postOutfit`, {
userId,
outfitName,
cart,
});
};
function addToCart(id) {
cart.push(id);
setCart(cart);
}
return <AnotherComponent addCartFunc={addToCart} />
}
Then you can use this method in the host component as follows:
export const Cart = ({addCartFunc}) => {
return (
<div className="product-name">
<button
className="button is-small is-outlined is-primary"
onClick={() =>
addCartFunc(product.id) & changeButtonText
}
>
{buttonText}
</button>
</div>
)
}

Related

make todo and store in localstorage but getting this error fetchdata.map is not a function at App (App.js:27:1)

import React, { useState, useEffect } from 'react';
import './style.css';
export default function App() {
const [state, setState] = useState([]);
const [inputData, setInputData] = useState();
const [fetchdata, setFetchData] = useState([])
const addHandler = () => {
setState((data) => {
return [...data, inputData];
});
localStorage.setItem('state', JSON.stringify(state));
setInputData('');
};
setFetchData(localStorage.getItem('state'))
return (
<div>
<input
onChange={(e) => setInputData(e.target.value)}
value={inputData || ''}
placeholder="add items"
/>
<button onClick={addHandler}>Add</button>
{fetchdata?.map((item) => {
return (
<div style={{ color: `#+${color}` }}>
<li key={item}>{item}</li>
</div>
);
}) || []}
</div>
);
}
This is the code I have tried also need dynamic colors for lists. Any help is appreciated with big thanks
even the key I have given unique but it says unique key required
Try to add a default value to your fetchData:
const [fetchdata, setFetchData] = useState(localStorage.getItem('state') ?? []);
and please don't begin to use useless useEffect like every begginer are doing, further documentation here !
try:
remove
setFetchData(localStorage.getItem('state'))
replace
const initData = () => {
try {
return JSON.parse(localStorage.getItem('state'));
} catch (e) {
return [];
}
}
const [fetchdata, setFetchData] = useState(initData())

New elements are not shown in the list after adding in React

When the user clicks the plus button, I want the task array to appear as a list. I am not able to push my tasks to an array
import logo from './logo.svg';
import './App.css';
import { BsPlusLg } from "react-icons/bs";
import { useState } from 'react';
const App = () => {
const items = ["Pizza", "Burger", "Shawarma", "Biryani", "Butter Naan", "Panner", "Chapathi"];
let tasks = [];
const [searchValue, setSearchValue] = useState("");
const changeValue = (event) => {
setSearchValue(event.target.value);
}
const searchedItems = tasks.filter((item) => {
if(item.toLowerCase().includes(searchValue.toLowerCase())){
return item;
}
})
const handleClick = () => {
if(items.length>0){
tasks.push(items.pop());
}
}
On clicking the plus button, after the items are added to the tasks array, they should appear as a list.
return (
<div className="App">
<div className='navbar'>
<input type="text" value={ searchValue } onChange={ changeValue } placeholder="Search"></input>
{/* <div className="verticalLine"></div> */}
<button onClick={ handleClick }><BsPlusLg /></button>
</div>
<hr/>
<div className='list'>
<ul>
{(searchValue.length>0)&&(searchedItems.map((item) => {
return <li key={item}>{item}</li>}))
}
{(searchValue.length===0)&&(tasks.map((item) => {
return <li key={item}>{item}</li>
}))
}
</ul>
</div>
</div>
);
}
export default App;
The elements in the tasks in the page are not being displayed when I log out of the tasks that are being pushed.
Clicking on your button does not update your component and will therefore rerender your list.
If if it would, it will run the whole function again and even call let tasks = []; again, which would reset your whole list. To update lists or whatever state you else need, you are required to use useState or similar hooks.
Because you also pop your items array, you might want to use two useState hooks and update them instead. Those will handle the internal state for you and isn't lost on a rerender. See https://reactjs.org/docs/hooks-state.html for more information. A solution might look like the following:
import "./styles.css";
import {useState} from 'react'
const BsPlusLg = () => <span >+</span>
const App = () => {
const [items, setItems] = useState( ["Pizza", "Burger", "Shawarma", "Biryani", "Butter Naan", "Panner", "Chapathi"]);
const [tasks, setTasks] = useState([]);
const [searchValue, setSearchValue] = useState("");
const changeValue = (event) => {
setSearchValue(event.target.value);
}
// searchedItems will be updated on each rerender
const searchedItems = tasks.filter((item) => {
if(item.toLowerCase().includes(searchValue.toLowerCase())){
return item;
}
})
const handleClick = () => {
if(items.length === 0){
// keep it simple and stop execution
return;
}
// enforce a new reference
let newItems = [...items];
let item = newItems.pop();
// enforce a new tasks reference
let newTasks = [...tasks, item];
setItems(newItems);
setTasks(newTasks);
}
return (
<div className="App">
<div className='navbar'>
<input type="text" value={ searchValue } onChange={ changeValue } placeholder="Search"></input>
{/* <div className="verticalLine"></div> */}
<button onClick={ handleClick }><BsPlusLg /></button>
</div>
<hr/>
<div className='list'>
<ul>
{(searchValue.length>0)&&(searchedItems.map((item) => {
return <li key={item}>{item}</li>}))
}
{(searchValue.length===0)&&(tasks.map((item) => {
return <li key={item}>{item}</li>
}))
}
</ul>
</div>
</div>
);
}
export default App;
Sandbox: https://codesandbox.io/s/confident-albattani-jf1z6f?file=/src/App.js:0-1655
It means that when React component be re-render, variables is not changed.
you must define 'item' variable as state.
const [tasks, setTasks] = useState([]);
For push it:
setTasks([...tasks, item.pop()])

React Firebase deleting the wrong document id

I've been trying to make a delete operation on a firebase database using Reactjs. I've got a bug with my function grabbing the wrong id from firebase.
I have a button that calls a handleOpen function which opens a Modal.
Modal operations:
// Grabs the right id
const [open, setOpen] = useState(false);
const handleOpen = (id) => {
console.log(id);
setOpen(true);
};
const handleClose = () => setOpen(false);
I've got a button that calls a handleDelete function which grabs the document id reference and deletes the document reference.
handleDelete function:
const handleDelete = (id) => {
const docRef = projectFirestore.collection("News").doc(id);
docRef.delete();
console.log("Deleted post data from id: " + id);
handleClose();
};
The Problem
From what I've been watching the handleDelete function grabs the last id from the mapped array of posts, it doesn't pass the id of the current document to the modal.
The problem only happens when I pass the function inside the modal. When I pass the function outside of the modal it works just fine.
The Objective
Grabbing document id, passing it to the modal and deleting the respective document.
Here's the full code:
import React, { useState } from "react";
import { projectFirestore } from "../../../../firebase/config";
import { useCollectionData } from "react-firebase-hooks/firestore";
import Layout from "../../../../hoc/Layout/Layout";
import { Link } from "react-router-dom";
import { Button, Box, Modal } from "#mui/material";
const DeletePost = () => {
const docRef = projectFirestore.collection("News");
const query = docRef.orderBy("createdAt", "desc");
const [posts] = useCollectionData(query, { idField: "id" });
// Modal operations
const [open, setOpen] = useState(false);
const handleOpen = (id) => {
setOpen(true);
};
const handleClose = () => setOpen(false);
const handleDelete = (id) => {
const docRef = projectFirestore.collection("News").doc(id);
docRef.delete();
console.log("Deleted post data from id: " + id);
handleClose();
};
return (
<Layout>
<ul>
{posts &&
posts.map((post) => {
const data = post.createdAt.toDate();
const day = data.getUTCDate();
const month = data.getUTCMonth();
const year = data.getUTCFullYear();
return (
<li key={post.id}>
<div>
<h3>{post.id}</h3>
<img
src={post.url}
alt={post.title}
/>
<p>
<b>
{day}/{month}/{year}
</b>{" "}
{post.description}
</p>
</div>
<div>
<Link to="/edit-post">
<Button>
Edit Post
</Button>
</Link>
<Button onClick={() => handleOpen()}>
Delete Post
</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="Delete"
aria-describedby="Delete Post"
>
<Box>
<div>
<h4>
Are you sure you want to delete {post.title}?
</h4>
</div>
<div>
<Button
onClick={() => {
debugger;
handleDelete(post.id);
}}
>
Yes
</Button>
<Button onClick={handleClose}>
No
</Button>
</div>
</Box>
</Modal>
</div>
</li>
);
})}
</ul>
</Layout>
);
};
export default DeletePost;
You could define a state variable that keeps tracks of the currently editing ID:
const [selectedId, setSelectedId] = useState(-1);
Then edit your handleOpen and handleClose functions:
const handleOpen = (id) => {
setSelectedId(id);
setOpen(true);
};
const handleClose = () => {
setSelectedId(-1);
setOpen(false);
};
In the handleDelete function, if an ID is selected, delete that one:
const handleDelete = (id) => {
const docRef = projectFirestore.collection('News').doc(selectedId !== -1 ? selectedId : id);
docRef.delete();
console.log('Deleted post data from id: ' + id);
handleClose();
};
Finally, you will need to update the handleOpen method in the JSX by adding the id parameter:
<Button onClick={() => handleOpen(post.id)}>
Delete Post
</Button>

React OnClick iteration

I want to do an onClick counter but I have a problem with the counter iterating correctly. In the app there are 3 "products" and after clicking "Add To Cart" button the state of the object is updated but all of the products are generated separately. I think that is cousing the problem where the counter is different for each of the products or everything will work correctly if I lift the state up, but the console.log is just freshly generated for all of the products. I'm not really sure so I need help with that.
Here is some code in the order from the parent to the last child:
import { useEffect, useState } from "react";
import ProductList from "./ProductList";
const Products = () => {
const [products, setProducts] = useState (null);
useEffect (() => {
fetch('http://localhost:8000/products')
.then(res => {
return res.json();
})
.then(data => {
setProducts(data);
})
}, []);
return (
<div className="ProductList">
{products && <ProductList products={products}/>}
</div>
);
}
export default Products;
import Card from "./Card";
const ProductList = (props) => {
const products = props.products;
return (
<div className="ProductList" >
{products.map((product) => (
<Card product={product} key={product.id} />))}
</div>
);
}
export default ProductList;
import { useState } from "react";
const Card= ({ product }) => {
const [showDescription, setShowDescription] = useState(false);
const [CartCounter, setCartCounter ] = useState(0);
console.log(CartCounter);
return (
<div className="Product-Preview" >
<div className="backdrop" style={{ backgroundImage: `url(${product.image})` }}></div>
<h2>{product.title}</h2>
<div>{product.price}</div>
<button className="ShowDescription" onClick={() => setShowDescription(!showDescription)}>Details</button>
<button className="AddToCart" onClick={() => setCartCounter(CartCounter + 1)}>Add To Cart </button>
{showDescription && <p>{product.description}</p>}
<br />
</div>
);
};
export default Card;
Ok, you want to keep track of an aggregated value. I'll list code in some high level.
const ProductList = () => {
const [count, setCount] = useState(0)
const addOrRemove = n => { setCount(v => v + n) }
return products.map(p => <Card addOrRemove={addOrRemove} />)
}
const Card = ({ addOrRemove }) => {
// optional if you want to track card count
// const [count, setCount] = useState(0)
return (
<>
<button onClick={() => { addOrRemove(1) }>Add</button>
<button onClick={() => { addOrRemove(-1) }>Remove</button>
</>
)
}
Essentially either you track the local count or not, you need to let the parent to decide what is the final count, otherwise there'll be some out of sync issue between the child and parent.

How to use useEffect on button Click?

I've to call useEffect / Fetch the data only when user click on Search Button otherwise not fetch the data..
Code:
const App = () => {
const[datas,setDatas] = useState([])
const [space,setSpace] = useState(null)
const [print, setPrint] = useState(false)
function getData(val){
// console.log(val.target.value)
setSpace(val.target.value);
setPrint(false)
}
// console.log(space)
useEffect(() => {
const fecthPosts = async () => {
let initial_url = `http://localhost:4000/search`
let url = initial_url + "?text=" + space
const res = await fetch(url);
const {result} = await res.json();
setDatas(result);
fecthPosts(); //I've to call this fetchPosts() when Btn is CLicked
},[space]);
return(
<div className="App">
{ //Displaying on search
print?
<>
<h2>{space}</h2>
<div>
{datas.map((field) =>
<p>{field.title}</p>
<p>{field.author}</p>
)}
</div>
</>
:null
}
<input type="text" onChange={getData} />
<button onClick={() => { setSpace(true); fetchPosts() }}>search</button>
</div>
)
}
};
export default App;
It's not working Error:
fetchPosts() is not defined...
I've also tried like this:
function trigger(){
useEffect(() => {
const fecthPosts = async () => {
let initial_url = `http://localhost:4000/search`
let url = initial_url + "?text=" + space
const res = await fetch(url);
const {result} = await res.json();
setDatas(result);
fecthPosts(); //I've to call this fetchPosts() when Btn is CLicked
},[space]);
}
<button onClick={() => { setSpace(true); trigger() }}>search</button>
It's not working Error:
React Hook useEffect has unnecessary dependencies:'space'
/PLZZ help to out...
make a separate function for api call and in your UseEffect function just call that function and on Button click function call the Api Function and it fetch data automatically
Use useCallback not useEffect
useCallback is similar to useEffect but is for when a function needs a callback, like what you're doing here onClick. useEffect is used in response to some prop changing not an action taken by a user.
You have to set your fetchPosts outside of the useEffect.
Then, you can use a new state search to track any click on the button.
const App = () => {
const [datas, setDatas] = useState([]);
const [space, setSpace] = useState(null);
const [print, setPrint] = useState(false);
const [search, setSearch] = useState(false);
const fetchPosts = async () => {
let initial_url = `http://localhost:4000/search`;
let url = initial_url + "?text=" + space;
const res = await fetch(url);
const { result } = await res.json();
setDatas(result);
};
function getData(val) {
setSpace(val.target.value);
setPrint(false);
}
useEffect(() => {
fetchPosts(); // fecthPosts is called each time space changed
}, [search]);
return (
<div className="App">
{
//Displaying on search
print ? (
<>
<h2>{space}</h2>
<div>
{datas.map((field) => (
<>
<p>{field.title}</p>
<p>{field.author}</p>
</>
))}
</div>
</>
) : null
}
<input type="text" onChange={getData} />
<button onClick={() => setSearch(!search)}>search</button>
</div>
);
};
export default App;
I initialized shouldFetchData = false. Once the button is clicked, I changed it's value; shouldFetchData = true. Inside useEffect I called fetchPosts() only when shouldFetchData is true.
import React, { useState } from "react";
const App = () => {
const [datas, setDatas] = useState([])
const [shouldFetchData, setShouldFetchData] = useState(false);
const [space, setSpace] = useState(null)
const [print, setPrint] = useState(false)
function getData(val) {
// console.log(val.target.value)
setSpace(val.target.value);
setPrint(false)
}
// console.log(space)
const fecthPosts = async () => {
let initial_url = `http://localhost:4000/search`
let url = initial_url + "?text=" + space
const res = await fetch(url);
const { result } = await res.json();
setDatas(result);
fecthPosts(); //I've to call this fetchPosts() when Btn is CLicked
}
useEffect(() => {
if(shouldFetchData) {
fecthPosts();
}
}, [space]);
return (
<div className="App">
{
print ?
<>
<h2>{space}</h2>
<div>
{datas.map((field) =>
<>
<p>{field.title}</p>
<p>{field.author}</p>
</>
)}
</div>
</>
: null
}
<input type="text" onChange={getData} />
<button onClick={() => {
setSpace(true);
setShouldFetchData(true);
}}>search</button>
</div>
)
};
export default App;
I found a few syntax errors in your code, so I hope I did what you intended.
If This is not the proper way to do this or if there exists a better way, please let me know. I'd be happy to learn🤗.

Categories

Resources