Here I am dispatching an action on button click but now I want to dispatch one more action from redux on the same button and the action which I want to dispatch is already imported on top named totalHandler so how am I supposed to do this thanks :)
import React from "react";
import { useParams } from "react-router-dom";
import "./ProductDetail.css";
import { useDispatch, useSelector } from "react-redux";
import { cartHandler } from "../../store/DataStore";
import { totalHandler } from "../../store/DataStore";
const Detail = () => {
const { id } = useParams();
const dispatch = useDispatch();
let data = useSelector((state) => state.data.DUMMY_DATA);
data = data.filter((val) => val.product_id === id);
data = data[0];
return (
<div className="detail_wrapper">
<div>
<img src={data.product_image} alt="" className="detail_image" />
</div>
<div className="inner">
<div className="detail_title">{data.product_title}</div>
<div className="detail_description">{data.product_description}</div>
<div className="detail_price">{data.product_price}</div>
<button
className="button"
onClick={() => dispatch(cartHandler(data.product_id))}
>
Add to Cart
</button>
</div>
</div>
);
};
export default Detail;
<button className="button" onClick={()=>{dispatch(cartHandler(data.product_id));dispatch(totalHandler())}}>Add to Cart</button>
OR create a function like
function Dispatch(){
dispatch(totalHandler());
dispatch(cartHandler(data.product_id));
}
<button className="button"
onClick={Dispatch}>Add to Cart</button>
Just add braquet at the right place {}
import React from "react";
import { useParams } from "react-router-dom";
import "./ProductDetail.css";
import { useDispatch, useSelector } from "react-redux";
import { cartHandler } from "../../store/DataStore";
import { totalHandler } from "../../store/DataStore";
const Detail = () => {
const { id } = useParams();
const dispatch = useDispatch();
let data = useSelector((state) => state.data.DUMMY_DATA);
data = data.filter((val) => val.product_id === id);
data = data[0];
return (
<div className="detail_wrapper">
<div>
<img src={data.product_image} alt="" className="detail_image" />
</div>
<div className="inner">
<div className="detail_title">{data.product_title}</div>
<div className="detail_description">{data.product_description}</div>
<div className="detail_price">{data.product_price}</div>
<button
className="button"
onClick={() => {dispatch(cartHandler(data.product_id));
dispatch(cartHandler(data.product_id_2))}}
>
Add to Cart
</button>
</div>
</div>
);
};
export default Detail;
One little precision because I have been struggling because of that detail, don't forget to await if necessary, in my case:
function handleClick(dice: DiceViewModel) {
return dice.isTenzies
? async () => {
await dispatch(initializeDice())
await dispatch(rollDice())
}
: () => dispatch(rollDice())
}
Related
I want to change the amount of a product in the preview CMP that is rendered by the list CMP.
The preview changes the data and the list (father CMP) I set the updated data in the state but the cmps didn't re-rendering. the data is changed and the state is changed but the cmps are not.
the father CMP
import { useEffect, useState } from 'react'
import { productService } from '../services/product.service.js'
import { ProductPreview } from './ProductPreview.jsx'
import { utilService } from '../services/util.service.js'
export function ProductList() {
const [totalMoney, setTotalMoney] = useState(0)
const [spendMoney, setSpendMoney] = useState('You havent Spent yet !')
const [products, setProducts] = useState([])
useEffect(() => {
setTotalMoney(217000000000)
getProducts()
}, [])
const getProducts = () => {
setProducts(productService.getProducts())
}
if (products.length === 0) return <h1>loading...</h1>
return (
<main>
<div className="total">
<span> Remaining: {utilService.currencyFormat(totalMoney)}</span>
<span>{spendMoney}</span>
</div>
<ul className="products-list">
{products.map((product) => (
<ProductPreview
product={product}
key={product._id}
getProducts={getProducts}
/>
))}
</ul>
</main>
)
}
the Previw CMP
import { utilService } from '../services/util.service.js'
import { productService } from '../services/product.service.js'
import { useEffect, useState } from 'react'
export function ProductPreview({ product, getProducts }) {
const [currProduct, setCurrProduct] = useState(product)
const changeAmount = (diff) => {
const newProduct = productService.increasAmount(currProduct._id, diff)
setCurrProduct(newProduct)
getProducts()
}
return (
<li className="product-preview">
<img src={currProduct.img} alt="" />
<h1>{currProduct.name}</h1>
<h1>{utilService.currencyFormat(currProduct.price)}</h1>
<div className="actions">
<button onClick={() => changeAmount(-1)} className="sell">
Sell
</button>
<p>{currProduct.amount}</p>
<button onClick={() => changeAmount(1)} className="buy">
Buy
</button>
</div>
</li>
)
}
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>
);
}
Two components talking to each other, when adding a product to the cart (component # 1), update the setState, via service (component # 2).
When adding the product to the cart, an error returns, saying that I don't have access to the export function of component # 1.
#1 Component
import React, { useContext, useEffect } from 'react';
import Link from 'next/link';
import { Clipboard } from 'react-feather';
import { OrderCartButton } from './styles';
import OrderService from '~/services/orderservice';
import Context from '~/utils/context';
function OrderCart() {
const { state, actions } = useContext(Context);
function updateQty() {
const qty = OrderService.count();
actions({ type: 'setState', payload: { ...state, value: qty } });
}
useEffect(() => {
updateQty();
}, []);
return (
<>
<Link href="/order">
<OrderCartButton data-order-qty={state.value}>
<Clipboard />
</OrderCartButton>
</Link>
</>
);
}
export default OrderCart;
#2 Component
import { reactLocalStorage } from 'reactjs-localstorage';
import { toast } from 'react-toastify';
class OrderService {
async add(product) {
const oldorder = reactLocalStorage.getObject('_order_meumenu');
if (oldorder.length) {
const merged = [...oldorder, ...product].reduce(
(r, { id, qty, title, description, price, image }) => {
const item = r.find((q) => q.id === id);
if (item) item.qty += qty;
else r.push({ id, qty, title, description, price, image });
return r;
},
[]
);
await reactLocalStorage.setObject('_order_meumenu', merged);
} else {
await reactLocalStorage.setObject('_order_meumenu', product);
}
toast.success('Produto adicionado ao Pedido');
const qty = await this.count();
return qty;
},
async count() {
const order = reactLocalStorage.getObject('_order_meumenu');
return order.length || 0;
},
}
export default OrderService;
Component #3 - Context Moved to callback
import React, { useState, useContext } from 'react';
import { Plus, Minus } from 'react-feather';
import { ProductContainer } from './styles';
import currency from '../../utils/currency';
import OrderService from '~/services/orderservice';
import Context from '~/utils/context';
function Product(product) {
const { state, actions } = useContext(Context);
const [qty, setQty] = useState(1);
function addProductOrder(elem, elemQty) {
// eslint-disable-next-line no-param-reassign
const newElem = [];
newElem.push({ ...elem, qty: elemQty });
OrderService.add(newElem).then((val) =>
actions({ type: 'setState', payload: { ...state, value: val } })
);
}
return (
<ProductContainer>
<div className="product-image">
<img
className="image"
src={product.image}
alt={product.title}
data-product-image
/>
</div>
<div className="product-details">
<div className="product-top">
<div className="product-title">
<span className="title" data-product-title>
{product.title}
</span>
<span className="desc" data-product-desc>
{product.description}
</span>
</div>
<button
type="button"
className="product-add"
onClick={() => addProductOrder(product, qty)}
>
<span className="btn -icon -rounded" title="Add Produto">
<Plus className="icon" />
</span>
</button>
</div>
<div className="product-bottom">
<div className="product-qty">
<div className="product-control-number">
<Minus className="icon" onClick={() => setQty(qty - 1)} />
<input
className="input"
type="number"
min="1"
max="9"
value={qty}
readOnly
data-number-value
/>
<Plus className="icon" onClick={() => setQty(qty + 1)} />
</div>
</div>
<div
className="product-price"
data-product-price={product.price}
data-product-totalprice="9"
>
{currency(product.price)}
</div>
</div>
</div>
</ProductContainer>
);
}
export default Product;
There are a few things to fix:
import OrderCart from '~/components/OrderCart';
// ...
OrderCart.updateQty();
the default export in ~/components/OrderCart is the class component(OrderCart) and updateQty is another function in the same file, so the import statement should be something like:
import { updateQty } from '~/components/OrderCart';
and the usage should be
updateQty()
but this will not work because calling a function that returns some object will not cause a rerender.
So, to fix this you should pass a callback to the child component that calls the add function, and call the callback after invoking add.
The callback function to pass as props to the child can be handleUpdateQty.
Okay so as you can tell,I am building a recipe app where in each recipe detail page, I can rate the the recipe...but now the problem is that when I rate one recipe now, the same rating is set for all the other recipes as well. Any idea how to fix that? Apreciate any advice...thanks so much :))
Details.js
import { useParams } from 'react-router-dom';
import './Details.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Image from './vitaminDfood-1132105308-770x553.jpg';
import {Link} from 'react-router-dom'
import ReactStars from 'react-rating-stars-component'
import { RecipeContext } from './RecipeContext';
import { useContext } from 'react';
function Details() {
const [details, setDetails] = useState([]);
const { recipeId } = useParams();
const{recipes,setRecipes}=useContext(RecipeContext)
const initialRatings = JSON.parse(localStorage.getItem("rating") || "[]");
const[rating,setRating]=useState(initialRatings)
useEffect(() => {
axios
.get(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}`)
.then((res) => setDetails(res.data));
});
useEffect(() => {
localStorage.setItem("rating", JSON.stringify(rating));
}, [rating])
const ratingChanged = (newRating) => {
var rate={
score:newRating
}
setRating(newRating)
axios.post(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}/ratings`,rate)
.then((res) => {
console.log(res.data)
setRecipes(recipes)
})
};
return (
<>
<div className="details">
<div className="food-photo">
<img src={Image} alt="" />
<Link to="/"> <i className="fas fa-arrow-left arrow"></i></Link>
<h1>{details.name}</h1>
</div>
<div className="star-line">
{new Array(rating).fill(null).map(() => (
<i className="fas fa-star stari"/>
))}
<p className="duration"><i className="far fa-clock"></i>{details.duration}<span>min.</span></p>
</div>
<p className="info">{details.info}</p>
<h1 className="ingredience">Ingredience</h1>
<div className="ing">{details.ingredients}</div>
<h1 className="ingredience">Příprava</h1>
<p className="description">{details.description}</p>
</div>
<div className="stars">
<ReactStars
classNames="star"
size={48}
onChange={ratingChanged}
count={5}
value={1}
edit
/>
</div>
</>
);
}
export default Details;
You have a couple options:
Store a different localStorage key for each recipe
Just have one large "ratings" key in localStorage and make that an object keyed off of your recipeId.
I think the first option is the most straightforward and can be accomplished with these minor modifications:
const initialRatings = JSON.parse(localStorage.getItem(`ratings-${recipeId}`) || "[]");
useEffect(() => {
localStorage.setItem(`ratings-${recipeId}`, JSON.stringify(rating));
}, [rating, recipeId])
I'm trying to build a react web application using functional components, react hooks, and redux. I can't figure out why I am getting the invalid hook call. When I click the Search button I get the react error.
Here's the entirety of the code:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'
export const SearchBar = () => {
const searchParameters = useSelector(state => state.searchParameters ?? {});
const dispatch = useDispatch;
return (
<div className="searchbar">
<div className="search-parameters">
<BasicSearch searchParameters={searchParameters} />
<div className="col form-group">
<button type="button" className="btn btn-primary" onClick={() => dispatch(doSearch())} >
SEARCH
</button>
</div>
</div>
</div>
);
}
const BasicSearch = (props) => {
return (
<>
<input type="text" id="origin" />
</>
);
}
const Actions = {
DO_SEARCH: "DO_SEARCH"
}
const doSearch = () => {
return { type: Actions.DO_SEARCH };
}
useDispatch on your 6th line of code is a function, so you need to assign the variable dispatch to it like so:
const dispatch = useDispatch();
Update
const dispatch = useDispatch();
and
onClick={() => dispatch(doSearch)}