make like "is this page useful " by reaction icons react - javascript

i need to make like reaction icon , when someone clicks in one icon the counter increase one and when he click to other icons it will decrease the previous icons then increase what wh click on .
so this is my code
it's will look like emojis with conuter for each one and you need to click to one of these emoji , then increase the count one .
import logo from './logo.svg';
import React, { useState } from 'react';
import './App.css';
let emojis = [
{
"id":"0",
"reactionName": "disLike",
"pic": "logo",
"PreCounter":20,
},
{
"id":"1",
"reactionName": "like",
"pic": "logo",
"PreCounter":2,
},
{
"id":"2",
"reactionName": "disLike",
"pic": "logo",
"PreCounter":0,
},
{
"id":"3",
"reactionName": "like",
"pic": "logo",
"PreCounter":20,
},]
function App() {
return (
<div className="App">
{
emojis.map(({id,reactionName, pic,PreCounter}) => {
return <Emoji
key={id}
reactionName={reactionName}
pic={pic}
PreCounter={PreCounter}
/>
})
}
</div>
);
}
export default App;
function Emoji (props){
const { key,reactionName, pic,PreCounter } = props;
const [count, setCounter] = useState(PreCounter);
const [selectedName, setSelectedName] = useState("noReaction");
const [selected, setSelected] = useState(false);
const handleClick = (e) => {
setSelectedName(e.target.getAttribute("name"));
if (selected) {
setCounter(count - 1);
setSelected(false);
}else {
setCounter(count + 1);
setSelected(true);
}
};
return(
<button onClick={handleClick} name={reactionName} value={count} id={key}>
<img src={pic} alt="logo" width="20"/>
{count}
</button>
);
}
I couldn't know how I can change the value of the previous click ,

I don't know if this is what you want
function Emoji(props) {
const { id, reactionName, pic, PreCounter, handleClick } = props;
return (
<button onClick={handleClick} name={reactionName+id} value={PreCounter} id={id}>
{/* <img src={pic} alt="logo" width="20"/> */}
{PreCounter}
</button>
);
}
let emojis = [
{
id: '0',
reactionName: 'disLike',
pic: 'logo',
PreCounter: 20,
},
{
id: '1',
reactionName: 'like',
pic: 'logo',
PreCounter: 2,
},
{
id: '2',
reactionName: 'disLike',
pic: 'logo',
PreCounter: 0,
},
{
id: '3',
reactionName: 'like',
pic: 'logo',
PreCounter: 20,
},
];
function useAppCount() {
const [list, listCallBack] = useState(emojis)
return [list, listCallBack]
}
function App() {
const [list, listCallBack] = useAppCount()
const handleClick = e => {
const id = e.target.getAttribute('id')
const data = list.map(r => {
if (r.isClick) {
r.isClick = false
r.PreCounter -= 1
}
if (r.id === id) {
r.isClick = true
r.PreCounter += 1
}
return r
})
listCallBack(() => data)
}
return (
<div className="App">
{list.map(({ id, reactionName, pic, PreCounter }) => {
return <Emoji key={id} id={id} reactionName={reactionName} pic={pic} PreCounter={PreCounter} handleClick={handleClick} />;
})}
</div>
);
}
export default App;

Related

React component does not update automatically in UI but only in array

I'm a newbie to react and I have a problem. When I try to add a new item to my list, the component updates but "closes", in fact to see the new item added to the list, I have to click again on the router link. At that point it updates. I have this problem in both the 'todos' component and the 'subtodos' component.
How can I do?
Data structure:
const datas = [
{
categoryTitle: "oggi",
categoryId: "1",
todos: [
{
todoName: "pulire",
todoId: "a",
completed: false,
subTodos: [
{ subTodoName: "pulire bagno", subTodoId: "a1", completed: false },
{ subTodoName: "pulire cucina", subTodoId: "a2", completed: false },
],
},
{
todoName: "cucinare",
todoId: "b",
completed: false,
subTodos: [
{ subTodoName: "cucinare primo", subTodoId: "a3", completed: false },
{
subTodoName: "cucinare secondo",
subTodoId: "a4",
completed: false,
},
],
},
],
},
{
categoryTitle: "pianificato",
categoryId: "2",
todos: [
{
todoName: "studiare",
todoId: "c",
completed: false,
subTodos: [
{
subTodoName: "studiare geometria",
subTodoId: "a1",
completed: false,
},
{
subTodoName: "studiare matematica",
subTodoId: "a2",
completed: false,
},
],
},
{
todoName: "spesa",
todoId: "d",
completed: false,
subTodos: [
{ subTodoName: "zucchine", subTodoId: "a3", completed: false },
{ subTodoName: "pasta", subTodoId: "a4", completed: false },
],
},
],
},
];
export default datas;
app.js
import "./App.css";
import {
Routes,
Router,
Route,
NavLink,
createBrowserRouter,
createRoutesFromElements,
RouterProvider,
} from "react-router-dom";
import { createContext, useState } from "react";
import CategoryLayout from "./layouts/CategoryLayout";
import Todos from "./pages/Todos";
import datas from "./datas/datas";
import SubTodos from "./pages/SubTodos";
export const DataContext = createContext();
function App() {
const [todoDatas, setTodoDatas] = useState(datas);
const [selectedItem, setSelectedItem] = useState();
const [todoId, setTodoId] = useState();
const [selectedTodo, setSelectedTodo] = useState();
return (
<DataContext.Provider
value={{
todoDatas,
setTodoDatas,
selectedItem,
setSelectedItem,
todoId,
setTodoId,
selectedTodo,
setSelectedTodo,
}}
>
<Routes>
<Route path="/" element={<CategoryLayout />}>
<Route path="todos" element={<Todos />}>
<Route path="subtodos" element={<SubTodos />} />
</Route>
</Route>
</Routes>
</DataContext.Provider>
);
}
export default App;
category layout
import React, { useContext, useState } from "react";
import { Outlet, Link } from "react-router-dom";
import { DataContext } from "../App";
import uniqid from "uniqid";
function CategoryLayout() {
const { todoDatas, setTodoDatas, setSelectedItem } = useContext(DataContext);
const [categoryInput, setcategoryInput] = useState(null);
const catchCategory = (value) => {
setcategoryInput(value);
};
const resetCategory = () => {
setcategoryInput("");
};
const addCategory = () => {
if (
categoryInput !== null &&
categoryInput !== "" &&
categoryInput !== undefined
) {
setTodoDatas((prev) => {
return [
...prev,
{
categoryTitle: categoryInput,
categoryId: uniqid(),
todos: [],
},
];
});
}
};
return (
<div className="app_wrap">
<div className="categories_wrap">
<h5>jessicamoretti#gmail.com</h5>
<input type="text" placeholder="search"></input>
{todoDatas?.map((category) => {
return (
<Link
to="todos"
key={category.categoryId}
id={category.categoryId}
onClick={() => {
setSelectedItem(
todoDatas.find((cat) => {
return cat.categoryId === category.categoryId;
})
);
}}
>
{category.categoryTitle}
</Link>
);
})}
<form
onClick={(e) => {
e.preventDefault();
addCategory();
}}
>
<input
type="text"
onChange={(e) => {
catchCategory(e.target.value);
}}
></input>
<button onClick={resetCategory}>Nuovo elenco</button>
</form>
</div>
<section>
<Outlet />
</section>
</div>
);
}
export default CategoryLayout;
todos
import React, { useEffect } from "react";
import { useParams } from "react-router-dom";
import { DataContext } from "../App";
import { useContext, useState } from "react";
import { Link, Outlet } from "react-router-dom";
import uniqid from "uniqid";
function Todos() {
const { setSelectedItem, selectedItem, todoId, setTodoId } =
useContext(DataContext);
const [todoInput, setTodoInput] = useState();
const catchTodo = (value) => {
setTodoInput(value);
};
const resetTodo = () => {
setTodoInput("");
};
const addTodo = (obj) => {
if (todoInput !== null && todoInput !== "" && todoInput !== undefined) {
setSelectedItem(
selectedItem?.todos?.push({
todoName: todoInput,
todoId: uniqid(),
completed: false,
subTodos: [],
})
);
}
};
console.log("todoid!", todoId);
return (
<div className="todos_subtodos_wrap">
<div className="todos_wrap">
{selectedItem?.todos?.map((todo) => {
return (
<div className="todo" key={todo.todoId}>
<Link
to="subtodos"
id={todo.todoId}
onClick={() => {
setTodoId(todo.todoId);
}}
>
{todo.todoName}
</Link>
<p>subtodo: 0 of {todo.subTodos?.length} </p>
</div>
);
})}
<form
className="todos_form"
onClick={(e) => {
e.preventDefault();
addTodo();
}}
>
<input
type="text"
className="todos_input"
placeholder="add todos"
onChange={(e) => {
catchTodo(e.target.value);
}}
></input>
<button className="add_todos" onClick={resetTodo}>
add todos
</button>
</form>
</div>
<Outlet />
</div>
);
}
export default Todos;
subtodos
import React, { useEffect } from "react";
import { useContext, useState } from "react";
import { DataContext } from "../App";
import uniqid from "uniqid";
function SubTodos() {
const {
setSelectedItem,
selectedItem,
todoId,
setTodoId,
selectedTodo,
setSelectedTodo,
} = useContext(DataContext);
const [subTodoInput, setSubTodoInput] = useState();
const [subTodos, setSubTodos] = useState(selectedTodo);
let selected = selectedItem?.todos?.find((el) => {
return el.todoId === todoId;
});
console.log(selectedItem, "selected item");
useEffect(() => {
setSelectedTodo(selected); // contiene il todo selezionato
}, [selected, setSelectedTodo]);
const catchSubTodo = (value) => {
setSubTodoInput(value);
};
const addSub = () => {
if (
subTodoInput !== null &&
subTodoInput !== "" &&
subTodoInput !== undefined
) {
setSelectedTodo(
selected?.subTodos?.push({
subTodoName: subTodoInput,
subTodoId: uniqid(),
completed: false,
})
);
}
};
return (
<div>
<div>
{selectedTodo?.subTodos?.map((el) => {
return (
<div key={el.subTodoId}>
<p> {el.subTodoName}</p>
</div>
);
})}
<form
onClick={(e) => {
e.preventDefault();
addSub();
}}
>
<input
type="text"
placeholder="+ Aggiungi sottoattività"
onChange={(e) => {
e.preventDefault();
catchSubTodo(e.target.value);
}}
></input>
<button
onClick={(e) => {
e.preventDefault();
}}
></button>
</form>
</div>
</div>
);
}
export default SubTodos;

Checkboxes are not working properly in React js

I am assigned a simple task to render permissions of different users using React JS.
There is a little problem and I think the page is not rendered according to the received props data.
Here is my code with little bit explanation.
// my App.js file
import { useState } from "react";
import "./App.css";
import Dropdown from "./components/Dropdown";
import Permissions from "./components/Permissions";
function App() {
const users = [
{
id: 1,
name: "Cirilla",
permissions: ["Post"],
},
{
id: 2,
name: "Michael Scofield",
permissions: ["Post", "Fetch", "Write"],
},
{
id: 3,
name: "Thomas Shellby",
permissions: [],
},
{
id: 4,
name: "Jon Snow",
permissions: ["Fetch", "Post"],
},
];
let [currentUser, setCurrentUser] = useState(users[0]);
const permissions = [
{
id: 1,
name: "Post",
val: currentUser.permissions.includes("Post"),
},
{
id: 2,
name: "Fetch",
val: currentUser.permissions.includes("Fetch"),
},
{
id: 3,
name: "Write",
val: currentUser.permissions.includes("Write"),
},
{
id: 4,
name: "Read",
val: currentUser.permissions.includes("Read"),
},
];
const dropDownChangeHandler = (value) => {
/*this function is executed whenever the dropdown is value is changed. setCurrentUser causes the app function to run again and the array permissions is created again according to the selected user*/
const user = users.find((item) => item.name === value);
setCurrentUser(user);
};
console.log(currentUser);
return (
<div className="container">
<Dropdown list={users} onChange={dropDownChangeHandler} />
<Permissions list={permissions} />
</div>
);
}
export default App;
Here is the permissions.js file
import PermissionsItem from "./PermissionsItem";
const Permissions = (props) => {
return (
<div>
{props.list.map((item) => (
<PermissionsItem key={item.id} item={item} />
))}
</div>
);
};
export default Permissions;
And finally, here is the permissionItem.js file
import React, { useEffect, useState } from "react";
const PermissionsItem = (props) => {
/* const [checkboxValue, setCheckBoxValue] = useState(props.item.val); // here useState does not provide the correct value so I have to use useEffect for this */
useEffect(() => {
setCheckBoxValue(props.item.val);
}, [props.item.val]);
const checkBoxChangeHandler = (event) => {
setCheckBoxValue(event.target.checked);
};
return (
<div className="permission-item">
<label htmlFor={props.item.id} className="perm-li">
{props.item.name}
</label>
<input
id={props.item.id}
type="checkbox"
checked={checkboxValue}
onChange={checkBoxChangeHandler}
/>
</div>
);
};
export default PermissionsItem;
The problem is that when I check any checkbox value of any user (suppose Cirilla), and then select the other user from dropdown (suppose Michael Scofield), the checked permission of first user, Cirilla, is also checked in Michael Scofield's permission. The Micheal's data is displayed correctly in the console but checkboxes are not rendered accordingly.
Please Help I have already wasted my whole week on this :( Any kind of help or suggestion is much appreciated. Thank you in advance !
The problem is that your do check by user in permission. There is no dependecy between the "permission" component and currentUser, so it does not render.
App:
const users = [
{
id: 1,
name: "Cirilla",
permissions: ["Post"],
},
{
id: 2,
name: "Michael Scofield",
permissions: ["Post", "Fetch", "Write"],
}
///...
];
const permissions = [
{
id: 1,
name: "Post"
},
{
id: 2,
name: "Fetch"
}
///...
];
const dropdownChange = (e) => {
setCurrentUser(users.find(x => x.id == e.target.value))
}
return (
<div className="App">
<div className="container">
<select value={currentUser?.id} onChange={dropdownChange} >
{users.map((item) => {
return <option value={item.id}>{item.name}</option>})
}
</select>
<Permissions list={permissions} currentUser={currentUser}/>
</div>
</div>
);
Permission and PermissionItem
const Permissions = (props) => {
return (
<div>
{props.list.map((item) => (
<PermissionsItem
key={item.id}
item={item}
hasItem={props.currentUser.permissions?.includes(item.name)}/>
))}
</div>
);
};
const PermissionsItem = (props) => {
const [checkboxValue, setCheckBoxValue] = useState(false);
useEffect(() => {
setCheckBoxValue(props.hasItem)
},[props.hasItem])
const checkBoxChangeHandler = (event) => {
//TODO users update
setCheckBoxValue(event.target.checked);
};
return (
<div className="permission-item">
<label htmlFor={props.item.id} className="perm-li">
{props.item.name}
</label>
<input
id={props.item.id}
type="checkbox"
checked={checkboxValue}
onChange={checkBoxChangeHandler}
/>
</div>
);

Avoid render the button after onClick

I'm trying to render some tags from a source and then put it into a buttons in random form but the issue comes after click the button my component re-render for no reason.
why can i do?...
const Blog = () => {
const [articles, setArticles] = useState([]);
const [tags, setTags] = useState([]);
// consult to database
const getData = async () => {
try {
// get aticles, tags
setArticles([
{ name: "title1", id_tag: 1 },
{ name: "title2", id_tag: 2 }
]);
setTags([
{ id: 1, name: "bell" },
{ id: 2, name: "fashion" }
]);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
getData();
}, []);
const getRandomTag = (i) => {
const newTags = tags;
const tag = newTags[Math.floor(Math.random() * newTags.length)];
return (
<button key={i} onClick={() => filterByTag(tag.id)}>
{tag.name}
</button>
);
};
// filter the article by tag
const filterByTag = (id) => {
setArticles(articles.filter((article) => article.id_tag === id));
};
return (
<>
<ul>
{articles.map((article, i) => (
<li key={i}>{article.name}</li>
))}
</ul>
<h4>TAGS</h4>
<ul>{tags.map((tag, i) => getRandomTag(i))}</ul>
</>
);
export default Blog;
https://codesandbox.io/s/heuristic-solomon-sp640j?from-embed=&file=/src/Blog.js
That's because your component re-renders whenever any state inside it or any props it receives changes either by value or reference. And whenever you map something without caching its value it maps on each rerender. You have tu either split it into other component or use caching.
import { useCallback, useEffect, useMemo, useState } from "react";
const Blog = () => {
const [articles, setArticles] = useState([]);
const [tags, setTags] = useState([]);
// consult to database
const getData = async () => {
try {
// get aticles, tags
setArticles([
{ name: "title1", id_tag: 1 },
{ name: "title2", id_tag: 2 },
{ name: "title3", id_tag: 3 },
{ name: "title4", id_tag: 4 },
{ name: "title5", id_tag: 5 },
{ name: "title6", id_tag: 6 }
]);
setTags([
{ id: 1, name: "bell" },
{ id: 2, name: "fashion" },
{ id: 3, name: "fancy" },
{ id: 4, name: "tag" },
{ id: 6, name: "name" },
]);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
getData();
}, []);
const filterByTag = useCallback((id) => {
setArticles(currentArticles => currentArticles.filter((article) => article.id_tag === id));
}, [setArticles]);
const getRandomTag = useCallback(
(i) => {
const newTags = tags;
const tag = newTags[Math.floor(Math.random() * newTags.length)];
return (
<button key={i} onClick={() => filterByTag(tag.id)}>
{tag.name}
</button>
);
},
[tags, filterByTag]
);
// filter the article by tag
const renderTags = useMemo(() => {
return tags.map((tag, i) => getRandomTag(i));
}, [tags, getRandomTag]);
return (
<>
<ul>
{articles.map((article, i) => (
<li key={i}>{article.name}</li>
))}
</ul>
<h4>TAGS</h4>
<ul>{renderTags}</ul>
</>
);
};
export default Blog;
Here you have a working example of caching https://codesandbox.io/s/sad-shadow-8pogoc?file=/src/Blog.js

update value of an object in an array - react hooks

I want to increment the counter value of an item on click, I've tried to find the solution in the docs and I watched tutorials but I can't find the solution.
FruitCounter.js
import { Fragment, useState } from "react"
import Fruit from "./Fruit"
const data = [
{ id: 1, name: "🍋", counter: 0 },
{ id: 2, name: "🍒", counter: 0 },
]
const FruitCounter = () => {
const [fruits, setFruits] = useState(data)
const clickHandler = (fruit) => {
// Increment 'counter value of clicked item by 1'
}
return (
<Fragment>
{fruits.map((fruit) => {
return (
<Fruit
key={fruit.id}
{...fruit}
clickHandler={() => clickHandler(fruit)}
/>
)
})}
</Fragment>
)
}
export default FruitCounter
Fruit.js
import React from "react"
const Fruit = ({ counter, name, clickHandler }) => {
return (
<button type="button" className="fruit" onClick={clickHandler}>
<p>{counter}</p>
<h2>{name}</h2>
</button>
)
}
export default Fruit
You can try this
const clickHandler = (fruit) => {
setFruits(
fruits.map((x) => {
if (x.id === fruit.id)
return {
...x,
counter: x.counter + 1,
};
return x;
})
);
};

Increase view count when each product is opened in React Redux

What i want is to increase the count of each product, when it is opened(viewed), using react redux.
AllProductsPage.js(The page starts here)
import React, { useState } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { Link } from "react-router-dom";
import ProductList from "./ProductList";
import Pagination from './Pagination'
import * as productActions from "../redux/actions/productActions";
import * as userActions from '../redux/actions/userActions'
import { Button } from "react-bootstrap";
import {FiSearch} from 'react-icons/fi'
import { Container, Row, Col} from "react-bootstrap";
const AllProductsPage =(props)=> {
const [quantity, showQuantity] = useState(true);
const [price, showPrice] = useState(true);
const [manufacturer,showManufacturer] = useState(true);
const data = {quantity,price,manufacturer};
const [search,setSearch]=useState("");
const loggedIn = props.loggedIn;
//Pagination Logic
const [currentPage,setCurrentPage] = useState(1)
const postsPerPage = 9
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = props.products.slice(indexOfFirstPost,indexOfLastPost)
//Change the page
const paginate =(pageNumber)=>{
setCurrentPage(pageNumber)
}
//const filteredSearch = props.products && props.products.filter(product=>product.name.toLowerCase().indexOf(search.toLowerCase())!==-1).sort( (a,b)=>(a.id>b.id)?1:-1 );
const filteredSearch = currentPosts && currentPosts.filter(product=>product.name.toLowerCase().indexOf(search.toLowerCase())!==-1).sort( (a,b)=>(a.id>b.id)?1:-1 );
return (
<div>
<div style={{"display":"flex","paddingTop":"30px"}} className="container">
{ loggedIn && <Link to="/addProduct"><Button variant="primary">Add Product</Button>{" "}</Link> }
<span style={{"marginLeft":"auto"}}><input type="text" onChange={event=>setSearch(event.target.value)}/> {" "} <FiSearch size="20px"/> </span>
</div>
<div style={{"display":"flex","justifyContent":"flex-end","alignItems":"space-between","paddingTop":"6px"}} className="container" >
<label style={{"padding":"0px 5px 0px 2px","color":"white"}}><input type="checkbox" defaultChecked={quantity} onClick={()=>showQuantity(!quantity)}/>{" "}Quantity</label>
<label style={{"padding":"0px 5px 0px 2px","color":"white"}}><input type="checkbox" defaultChecked={price} onClick={()=>showPrice(!price)}/>{" "}Price </label>
<label style={{"padding":"0px 5px 0px 2px","color":"white"}}><input type="checkbox" defaultChecked={manufacturer} onClick={()=>showManufacturer(!manufacturer)}/>{" "}Manufacturer </label>
</div>
<hr></hr>
<div style={{minHeight:"100vh"}}>
<ProductList
products={filteredSearch}
data={data}
togglePrice={showPrice}
toggleQuantity={showQuantity}
toggleManufacturer={showManufacturer}
loggedIn={props.loggedIn}
/>
<br />
<Container>
<Row>
<Col></Col>
<Col xs="auto" sm="auto" md="auto" lg="auto">
<Pagination postsPerPage={postsPerPage} totalPosts={props.products.length} paginate={paginate} />
</Col>
<Col></Col>
</Row>
</Container>
</div>
<footer>
<p style={{"textAlign":"center","backgroundColor":"#333","color":"white","padding":"20px"}}>Copyright #2020, Rohit K F</p>
</footer>
</div>
);
}
function mapStateToProps(state, ownProps) {
return {
products: state.products,
users : state.users
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(productActions, dispatch),
userAction : bindActionCreators(userActions,dispatch)
};
}
export default (connect(mapStateToProps, mapDispatchToProps))(AllProductsPage);
ProductList.js(then it takes each product and passes it to Product.js)
import React from "react";
import Product from "./Product";
import { Container, Row, Col} from "react-bootstrap";
const chunk = (arr, chunkSize = 1, cache = []) => {
const tmp = [...arr]
if (chunkSize <= 0) return cache
while (tmp.length) cache.push(tmp.splice(0, chunkSize))
return cache
}
const ProductList = (props) => {
const productsChunks = chunk(props.products, 3)
const rows = productsChunks.map((productChunk, index) => {
const productsCols = productChunk.map((product, index) => {
return (
<Col xs="auto" sm="auto" md="auto" lg="auto" key={product.id} style={{"paddingBottom":"20px"}}>
<Product
key={product.id}
id={product.id}
quantity={product.quantity}
price={product.price}
name={product.name}
description={product.description}
manufacturer={product.manufacturer}
{...props}
/>
</Col>
);
});
return (
<Row key={index} style={{"paddingBottom":"20px"}}>
{productsCols}
</Row>
)});
return (
<Container>
{rows}
</Container>
)
}
export default ProductList;
Product.js(Here we show the each product)
import React,{useState} from "react";
import { Link } from "react-router-dom";
import { Prompt, withRouter } from "react-router";
import { connect } from "react-redux";
import * as productActions from "../redux/actions/productActions";
import { bindActionCreators } from "redux";
import { Card, Button } from "react-bootstrap";
import toastr from "toastr";
import EditProduct from './EditProduct'
import {MdDelete,MdVisibility,MdCreate} from 'react-icons/md'
const Product = (props) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const isLoggedIn = props.loggedIn
const checkUser = (e) => {
if (!isLoggedIn) {
e.preventDefault();
toastr.options = { positionClass: "toast-top-full-width",hideDuration: 300,timeOut: 2000,};
toastr.clear();
setTimeout(() => toastr.warning("Login to view details"), 0);
}
};
const deleteProduct = () => {
props.actions.deleteProduct(props.id)
};
//<Link to={'/ProductDetail/'+props.id} >
const product = {
id :props.id,name:props.name,quantity:props.quantity,description:props.description,manufacturer:props.manufacturer,price:props.price
}
return (
<>
<Card style={{ width: "18rem", "borderRadius":"30px","border":"3px solid" }}>
{isLoggedIn && (
<Prompt when={isLoggedIn}
message={(location) => location.pathname.includes("/ProductDetail/") ? `Are you sure you want to view the details ?` : true }
/>
)}
<Card.Body>
<Card.Title style={{"fontSize":"30px","fontWeight":"bold","display":"flex", "justifyContent":"center"}}> {props.name} </Card.Title>
{props.data.quantity && ( <Card.Text> Quantity : {props.quantity} </Card.Text> )}
{props.data.manufacturer && <Card.Text> Manufacturer : {props.manufacturer}</Card.Text>}
{props.data.price && <Card.Text>$ {props.price}</Card.Text>}
<div style={{ display: "flex", justifyContent: "space-around" }}>
<Link
to={{
pathname: `/ProductDetail/${props.id}`,
productName: {
id: props.id,
name: props.name,
price: props.price,
quantity: props.quantity,
description: props.description,
manufacturer: props.manufacturer,
},
}}
>
<Button variant="primary" onClick={(event) => checkUser(event)} style={{ "fontWeight":"bold" }} >
{!isLoggedIn && <span style={{"paddingRight":"5px"}}>View</span> }
{!isLoggedIn && <MdVisibility color="black"/> }
{isLoggedIn && <MdVisibility/>}
</Button>
</Link>
{isLoggedIn && <Button variant="success" style={{"fontWeight":"bold" }} onClick={() => handleShow()} ><MdCreate/></Button> }
{isLoggedIn && <Button variant="danger" style={{"fontWeight":"bold" }} onClick={() => deleteProduct()} ><MdDelete/> </Button>}
</div>
</Card.Body>
</Card>
<EditProduct show={show} handleClose={handleClose} actions={props.actions} product={product}/>
</>
);
};
function mapStateToProps(state, ownProps) {
return {
products: state.products,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(productActions, dispatch),
};
}
export default connect(mapStateToProps,mapDispatchToProps)(withRouter(Product));
ProductDetail.js(When clicked on View, it goes to this page to view details of the product)
import React from 'react';
import { Link} from 'react-router-dom';
import {withRouter} from 'react-router'
import {Button, Card} from 'react-bootstrap'
const ProductDetail=(props)=>{
console.log(props)
const style={"display":"flex", "justifyContent":"center","alignItems":"center"}
return(
<div style={style}>
<Card style={{ width: "18rem","borderRadius":"30px" }}>
<Card.Body style={{style}}>
<Card.Title style={{"fontSize":"30px","fontWeight":"bold","display":"flex", "justifyContent":"center"}}> {props.location.productName.name} </Card.Title>
<Card.Text><strong>Quantity :</strong>{props.location.productName.quantity}</Card.Text>
<Card.Text><strong>Price :</strong>{props.location.productName.price}</Card.Text>
<Card.Text><strong>Manufacturer:</strong>{props.location.productName.manufacturer}</Card.Text>
<Card.Text><strong>Description :</strong>{props.location.productName.description}</Card.Text>
<div>
<Link to="/"><Button variant="primary" style={{ height: "6vh","fontWeight":"bold" }}>Back</Button></Link>
</div>
</Card.Body>
</Card>
</div>
);
}
export default withRouter(ProductDetail);
ProductReducer.js
import initialState from "./initialState";
import * as actionTypes from "../actions/actionTypes";
export default function productReducer(state = initialState.products, action) {
switch (action.type) {
case actionTypes.INIT:
return action.products;
case actionTypes.ADD:
return [...state, Object.assign({}, action.product)];
case actionTypes.DELETE:
return [...state.filter((product) => product.id !== action.id)];
case actionTypes.UPDATE:
return [
...state.filter((product) => product.id !== action.product.id),
Object.assign({}, action.product),
];
case actionTypes.VIEW:
return [
...state[action.product.id],
Object.assign({},action.product.view)
]
default:
return state;
}
}
ProductActions.js
import dataApi from "../../server/dataAPI";
import * as actionTypes from "../actions/actionTypes";
//======================LOADING A PRODUCT
export function loadProduct() {
return function (dispatch) {
return dataApi
.getAllProducts()
.then((products) => {
dispatch({ type: actionTypes.INIT, products });
})
.catch((error) => {
throw error;
});
};
}
//==========================ADDING A PRODUCT
export function addProduct(product) {
return function (dispatch) {
return dataApi
.addProduct(product)
.then((product) => {
dispatch({ type: actionTypes.ADD, product });
})
.catch((error) => {
throw error;
});
};
}
//==========================DELETE A PRODUCT
export function deleteProduct(id) {
return function (dispatch) {
return dataApi
.deleteProduct(id)
.then((product) => {
dispatch({ type: actionTypes.DELETE, id});
})
.catch((error) => {
throw error;
});
};
}
//==========================UPDATE A PRODUCT
export function updateProduct(product) {
return function (dispatch) {
return dataApi
.updateProduct(product)
.then((product) => {
dispatch({ type: actionTypes.UPDATE, product });
})
.catch((error) => {
throw error;
});
};
}
//Increase View Count of product
export function addView(product){
return function (dispatch){
return dataApi.addView(product)
.then(product=>{
dispatch({type:actionTypes.VIEW, product})
})
}
}
dataAPI.js(to add,delete,update to json server with axios)
import axios from 'axios'
class dataAPI {
static getAllProducts() {
return axios.get('http://localhost:4000/products?_sort=id&_order=asc').then(response=>response.data);
}
static addProduct(product) {
return axios.post('http://localhost:4000/products',product).then(response=>response.data);
}
static updateProduct(product){
return axios.patch('http://localhost:4000/products/'+product.id,product)
.then(response=>response.data);
}
static deleteProduct(id){
return axios.delete(`http://localhost:4000/products/${id}`).then(response=>response.data);
}
static getAllUsers(){
return axios.get('http://localhost:4000/users').then(response=>response.data);
}
static addUser(user) {
return axios.post('http://localhost:4000/users',user).then(response=>response.data);
}
}
export default dataAPI;
db.json(the file that contains all the data)
{
"products": [
{
"id": 1,
"name": "Moto G5 Ultra",
"quantity": 3,
"price": 10000,
"description": "Moto G5",
"manufacturer": "Motorola",
"views" : 0
},
{
"id": 2,
"name": "Racold Geyser",
"quantity": 2,
"price": 60000,
"description": "Moto G5",
"manufacturer": "Motorola",
"views" : 0
},
{
"name": "Lenovo G5",
"quantity": 3,
"price": 55000,
"manufacturer": "Lenovo",
"description": "A gaming laptop",
"id": 3,
"views" : 0
},
{
"name": "Acer Swift ",
"quantity": 5,
"price": 35000,
"manufacturer": "Acer",
"description": "Business Laptop",
"id": 4,
"views" : 0
},
{
"name": "Acer Nitro 7",
"quantity": 4,
"price": 75000,
"manufacturer": "Acer",
"description": "A gaming laptop",
"id": 5,
"views" : 0
},
"users": [
{
"id": 1,
"email": "vi#gmail.com",
"password": "truth",
"name": {
"firstName": "Rick",
"lastName": "Garner"
},
"location": "Canada",
"mobile": "55643980"
},
{
"id": 2,
"email": "t#t.com",
"password": "123",
"name": {
"firstName": "Ram",
"lastName": "Shankar"
},
"location": "Delhi",
"mobile": "9895454860"
},
{
"email": "e#e.com",
"password": "123456789",
"name": {
"firstName": "RAGAV",
"lastName": "Shant"
},
"location": "Karnataka",
"mobile": "1234567891",
"id": 3
},
{
"email": "k#k.com",
"password": "123456789",
"name": {
"firstName": "sd",
"lastName": "dv"
},
"location": "dfv",
"mobile": "12345678231",
"id": 4
}
]
}
You may want to dispatch update products action in useEffect inside ProductDetail.jsx page.
useEffect(() => {
updateProduct({
...props.location.productName,
views: props.location.productName + 1,
});
}, []);
Of course you will also need to pass views from Product.jsx.
This will increase views every time user opens/refreshes page.
EDIT:
If you want to have separate API endpoint for incrementing view count, you can implement its increment logic on server side. In that case, it won't change anything in current reducer file ProductReducer.js.
But I think there is no need for it. You can use updateProduct API , just for this reason. No need to change reducer in this case also.
EDIT 2:
If addView API is returning product id and incremented view, then you you can write reducer as -
case actionTypes.VIEW:
return [
...state.map((product) => {
if (product.id === action.product.id) {
product.views = action.product.views;
}
return product;
})
]
So what i did was I added a useEffect() to my ProductDetail.js file and fired the Action from there.
ProductDetail.js
import React,{useEffect} from 'react';
import { Link} from 'react-router-dom';
import {withRouter} from 'react-router'
import {Button, Card} from 'react-bootstrap'
import { connect } from "react-redux";
import * as productActions from "../redux/actions/productActions";
import { bindActionCreators } from "redux";
const ProductDetail=(props)=>{
useEffect(() => {
console.log("PROPIES ",props.location.productName.id+" "+props.location.productName.views)
props.actions.addView(props.location.productName.id,props.location.productName.views)
},[props.actions,props.location.productName.id,props.location.productName.views])
const style={"display":"flex", "justifyContent":"center","alignItems":"center","minHeight":"100vh"}
return(
<div style={style}>
<Card style={{ width: "18rem","borderRadius":"30px" }} >
<Card.Body style={{style}}>
<Card.Title style={{"fontSize":"30px","fontWeight":"bold","display":"flex", "justifyContent":"center"}}> {props.location.productName.name} </Card.Title>
<Card.Text><strong>Quantity :</strong>{props.location.productName.quantity}</Card.Text>
<Card.Text><strong>Price :</strong>{props.location.productName.price}</Card.Text>
<Card.Text><strong>Manufacturer:</strong>{props.location.productName.manufacturer}</Card.Text>
<Card.Text><strong>Description :</strong>{props.location.productName.description}</Card.Text>
<div>
<Link to="/"><Button variant="primary" style={{ height: "6vh","fontWeight":"bold" }}>Back</Button></Link>
</div>
</Card.Body>
</Card>
</div>
);
}
function mapStateToProps(state, ownProps) {
return {
products: state.products,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(productActions, dispatch),
};
}
export default connect(mapStateToProps,mapDispatchToProps)(withRouter(ProductDetail));
It then fires this action
//Increase View Count of product
export function addView(id,count){
console.log("func called")
return function (dispatch){
console.log("api to be called")
return dataApi.addView(id,count)
.then((product)=>{
console.log("dispatched")
dispatch({type:actionTypes.VIEW, id: product.id})
})
}
}
So it updates the view on the server first and then in the reducer state
dataAPI.js
static addView(id,count){
return axios.patch('http://localhost:4000/products/'+id,{views:count+1})
.then(response=>response.data);
}
productReducer.js
import initialState from "./initialState";
import * as actionTypes from "../actions/actionTypes";
export default function productReducer(state = initialState.products, action) {
switch (action.type) {
case actionTypes.INIT:
return action.products;
case actionTypes.ADD:
return [...state, Object.assign({}, action.product)];
case actionTypes.DELETE:
return [...state.filter((product) => product.id !== action.id)];
case actionTypes.UPDATE:
return [
...state.filter((product) => product.id !== action.product.id),
Object.assign({}, action.product),
].sort( (a,b)=>(a.id>b.id)?1:-1 );
case actionTypes.VIEW:
let prod = [...state][action.id-1];
prod.views++;
//eslint-disable-next-line
let addView =()=>( [
...state.filter(product => product.id !== action.id),
Object.assign({}, prod)
])
return state;
default:
return state;
}
}
I had to write the ActionType.VIEW case in switch like this
case actionTypes.VIEW:
let prod = [...state][action.id-1];
prod.views++;
//eslint-disable-next-line
let addView =()=>( [
...state.filter(product => product.id !== action.id),
Object.assign({}, prod)
])
return state;
I had to put the state modification part inside a function called addView(), otherwise I saw that the function was repeatedly getting called infinitly. I'd appreciate it is someone could help me with a around that

Categories

Resources