How to render a icon dynamically using React Redux - javascript

I'm creating a playlist and I want to show my "favorite tracks" with a different icon, like a heart filled, by default render a bordered heart. Basically the default code is working but when refresh the page, the icon filled is gone and render the default.
And if anyone has a tip, i'm new in react and dont know if I can have many "useStates" like that.
I forgot to mention, the app are using Redux-persist, so the info continues in the store after the page refresh. That is why I want show the icon filled based on info at the store
import React, { useState, useEffect } from "react";
import ReactDOMServer from "react-dom/server";
import axios from "axios";
import { useSelector, useDispatch } from "react-redux";
import { ListContainer, PlayerStyle, InfoBox } from "./style.jsx";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
import FavoriteIcon from "#material-ui/icons/Favorite";
import PlayCircleOutlineIcon from "#material-ui/icons/PlayCircleOutline";
import PauseCircleOutlineIcon from "#material-ui/icons/PauseCircleOutline";
import MusicPlayer from "../MusicPlayer/index";
const List = () => {
const [isLoading, setLoading] = useState(true);
const [valueList, setValueList] = useState(20);
const [search, setSearch] = useState("");
const [dataList, setDataList] = useState([]);
const [trackList, setTrackList] = useState([]);
const [bannerAlbum, setBannerAlbum] = useState("");
const [createdBy, setCreatedBy] = useState("");
const [isPlayed, setIsPlayed] = useState(false);
const [musicPlayed, setMusicPlayed] = useState("");
const [showTrackList, setShowTrackList] = useState(true);
const [showFavoriteList, setShowFavoriteList] = useState(false);
const [favoriteData, setFavoriteData] = useState([]);
const favoriteList = useSelector(
(state) => state.FavoriteListReducer.favorites
);
const dispatch = useDispatch();
let chartPlaylist = `https://api.deezer.com/playlist/1111141961?&limit=${valueList}`;
useEffect(() => {
axios.get(chartPlaylist).then((res) => {
// console.log(res.data, "album");
setDataList(res.data);
setTrackList(res.data.tracks.data);
setBannerAlbum(res.data.picture_medium);
setCreatedBy(res.data.creator.name);
// Ao terminar a requisição dos dados, mostra os componentes
setLoading(false);
});
}, [chartPlaylist]);
useEffect(() => {
axios.all(favoriteList.map((l) => axios.get(l))).then(
axios.spread(function (...res) {
// all requests are now complete
setFavoriteData(res);
})
);
if (!showTrackList && favoriteList.length === 0) {
setShowTrackList(true);
setShowFavoriteList(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [favoriteList]);
const handleInput = (e) => {
setSearch(e.target.value);
};
const handlePlayed = () => {
if (!isPlayed) {
setIsPlayed(true);
} else {
setIsPlayed(false);
}
};
const handleIconSwap = (e) => {
e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
!isPlayed ? <PlayCircleOutlineIcon /> : <PauseCircleOutlineIcon />
);
};
const Add_fav = (e) => {
dispatch({
type: "ADD_FAV",
newId: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
"value"
)}`,
});
e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
""
) : (
<FavoriteIcon style={{ color: "red" }} />
)
);
};
const Del_fav = (e) => {
dispatch({
type: "DELETE_FAV",
remove: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
"value"
)}`,
});
e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
""
) : (
<FavoriteBorderIcon fontSize={"inherit"} />
)
);
};
// eslint-disable-next-line no-array-constructor
const handleFav = (e) => {
favoriteList.includes(
`https://api.deezer.com/track/${e.currentTarget.getAttribute("value")}`
)
? Del_fav(e)
: Add_fav(e);
};
const toggleData = () => {
if (showTrackList && favoriteList.length > 0) {
setShowTrackList(false);
setShowFavoriteList(true);
}
if (!showTrackList) {
setShowTrackList(true);
setShowFavoriteList(false);
}
};
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<button
onClick={() => {
if (valueList < 100) {
setValueList(valueList + 20);
}
}}
>
adicionar +20 musicas
</button>
<br />
<br />
<br />
<button
onClick={() => {
toggleData();
}}
>
Mostrar Favoritos
</button>
<InfoBox>
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<div className="content">
<img src={bannerAlbum} alt="" />
<div className="Info--desc">
<h1>{dataList.title}</h1>
<div>
<span>criado por: </span>
{createdBy}
</div>
</div>
</div>
</InfoBox>
<ListContainer>
<div className="headerList">
<div className="rank">#</div>
<div className="favorite--icon">
{/* <FavoriteBorderIcon fontSize={"inherit"} /> */}
</div>
<div className="title--music">FAIXA</div>
<div className="title--artist">ARTISTA</div>
<div className="title--album">ALBUM</div>
<div className="title--duration">D.</div>
</div>
<div className="bodyList">
{showTrackList &&
trackList.map((item, key) => {
return (
<>
<div
key={key}
onMouseEnter={handleIconSwap}
onMouseLeave={(e) => {
if (!isPlayed) {
e.currentTarget.firstElementChild.innerHTML = key + 1;
} else {
e.currentTarget.firstElementChild.innerHTML =
ReactDOMServer.renderToString(
<PauseCircleOutlineIcon />
);
}
}}
>
<div
className="rank"
onClick={() => setMusicPlayed(trackList[key].preview)}
>
{key + 1}
</div>
<div className="favorite--icon">
// Here that has to dynamically render
<span value={item.id} onClick={(e) => handleFav(e)}>
<Icon favIco={false} />
</span>
</div>
<div className="title--music">
<a target="_blank" rel="noreferrer" href={item.link}>
{item.title}
</a>
</div>
<div className="title--artist">{item.artist.name}</div>
<div className="title--album">{item.album.title}</div>
<div className="title--duration">
{item.duration / 60 < 10
? "0" +
(item.duration / 60)
.toFixed(2)
.toString()
.replace(".", ":")
: (item.duration / 60)
.toFixed(2)
.toString()
.replace(".", ":")}
</div>
</div>
</>
);
})}
</div>
</ListContainer>
</>
);
};
const Icon = (props) => {
switch (props.favIco) {
case true:
return <FavoriteIcon style={{ color: "red" }} />;
case false:
return <FavoriteBorderIcon fontSize={"inherit"} />;
default:
return <FavoriteBorderIcon fontSize={"inherit"} />;
}
};
export default List;
My Reducer store is working fine, just not render the heart filled on load the page, if I click at heart and remove the info from the store and click again the icon change to filled again.
const initialState = {
favorites: [],
};
const List = (state = initialState, action) => {
switch (action.type) {
case "ADD_FAV":
return { ...state, favorites: [...state.favorites, action.newId] };
case "DELETE_FAV":
let index = state.favorites.indexOf(action.remove);
if (index > -1) {
state.favorites.splice(index, 1);
}
return {
...state,
favorites: [...state.favorites],
};
default:
}
return state;
};
export default List;
Thank you all

I've seen in your comment above that you're currently using redux-persist to keep the store between reloads, so having access to the data is not the problem.
Looking your code I think the problem might be with the way you're rendering the icons.
If I understood correctly, that is done by the Add_fav function:
const Add_fav = (e) => {
dispatch({
type: "ADD_FAV",
newId: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
"value"
)}`,
});
e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
""
) : (
<FavoriteIcon style={{ color: "red" }} />
)
);
};
But this function only runs when you add a new favorite - hence the absence of the icons after a reload, because this part of the code would not be ran at all.
My suggestion would be to change the icon rendering logic to the return JSX of the component, instead of inside the event handler function. Something like this:
<span value={item.id} onClick={(e) => handleFav(e)}>
{
favoriteList.includes(item.id)
? <FavoriteIcon style={{ color: "red" }} />
: <FavoriteBorderIcon fontSize={"inherit"} />
}
</span>

So figured out I was send to the store was a string and at the render logic was searching for a int number, then a edited the function add the method parseInt()
const Add_fav = (e) => {
dispatch({
type: "ADD_FAV",
newId: parseInt(e.currentTarget.getAttribute("value")),
});
};
and now works fine bacause the logic is searching for a Number Int.
<span value={item.id} onClick={(e) => handleFav(e)}>
{
favoriteList.includes(item.id)
? <FavoriteIcon style={{ color: "red" }} />
: <FavoriteBorderIcon fontSize={"inherit"} />
}
</span>

Related

React Testing Library / Jest - Child component not present when rendering parent component

I'm trying to test that a click handler in my child component that is changing the parent component's state and then displaying conditional jsx in my parent component, but I can't figure out the best way to do so and I'm also having trouble debugging. My other tests that test the parent component and child component separately are working (as in I'm able to find dom elements that I expect to be present), but when I try to test the clicking of a button in the child component by rendering the parent component, my test fails.
Expected behavior:
User clicks the div with className 'open-comparison-btn'
Child component calls the props.setModalShowing function with 'true'
Parent component modalShowing state is updated to true
Parent component re-renders and displays the conditional jsx className 'comparison-modal'
The functionality is working in the localhost browser, but not in my test, and I can't even find the child component's html at all in my test.
Parent component:
import React, { useState, useEffect } from 'react';
import ProductCard from './ProductCard.jsx';
const RelatedProducts = (props) => {
const [position, setPosition] = useState(0);
const componentName = 'RelatedProducts';
const [modalShowing, setModalShowing] = useState(false);
const [currentProduct, setCurrentProduct] = useState({});
const [comparisonProduct, setComparisonProduct] = useState({});
useEffect(() => {
setCurrentProduct(props.currentProduct);
}, [props.currentProduct]);
const getFeatures = () => {
return [...currentProduct.features, ...comparisonProduct.features]
.filter((v, i, a)=>a.findIndex(v2=>(v.feature === v2.feature && v.value === v2.value)) === i);
};
return (
<>
<div className='related-products-container' role="listbox" aria-label="related products" style={{marginLeft: `-${position}px`}}>
{props.relatedProducts ?
props.relatedProducts.map((product) => {
return <ProductCard
modalShowing={modalShowing}
setModalShowing={setModalShowing}
setComparisonProduct={setComparisonProduct}
key={product.id}
product={product}
generateStars={props.generateStars}
isFetching={props.isFetching}
setIsFetching={props.setIsFetching}
parentComponent={componentName}
yourOutfit={props.yourOutfit}
addToOutfit={props.addToOutfit}
/>;
})
: null
}
</div>
<div className='fade-top'>
{ position > 0 ?
<div className="arrow-container-left" role="button" aria-label="scroll left" onClick={() => { setPosition(position - 250); }}>
<div className="arrow-left"></div>
</div>
: null
}
{ props && props.relatedProducts && position <= (props.relatedProducts.length - 4) * 250 ?
<div className="arrow-container-right" role="button" aria-label="scroll right" onClick={() => { setPosition(position + 250); }}>
<div className="arrow-right"></div>
</div>
: null
}
</div>
{modalShowing ?
<div className='comparison-modal' role='dialog' aria-label='comparison window'>
<div className='modal-top'>COMPARING</div>
<div className='modal-product-names'>
<div className='product-1'>{currentProduct.name}</div>
<div className='product-2'>{comparisonProduct.name}</div>
</div>
<table className='modal-table'>
<tbody>
{getFeatures().map((feature, index) => {
return (
<tr key={`${feature}-${index}`}>
<td className='left-check'>{currentProduct.features.filter(item => item.feature === feature.feature && item.value === feature.value).length > 0 ? '✓' : null}</td>
<td>{feature.value} {feature.feature}</td>
<td className='right-check'>{comparisonProduct.features.filter(item => item.feature === feature.feature && item.value === feature.value).length > 0 ? '✓' : null}</td>
</tr>
);
})}
</tbody>
</table>
<div className="close-btn" onClick={() => { setModalShowing(false); }}></div>
</div>
: null}
</>
);
};
export default RelatedProducts;
Child component:
import React, { useState, useEffect } from 'react';
import ratingsAPI from '../../API/Ratings.js';
import { useNavigate } from 'react-router-dom';
const ProductCard = (props) => {
const navigate = useNavigate();
const [averageRating, setAverageRating] = useState();
const stars = props.generateStars(averageRating, 'related');
useEffect(() => {
ratingsAPI.getReviewMetadata(props.product.id)
.then((metadata) => {
setAverageRating(getAverageRating(metadata.ratings));
props.setIsFetching(false);
});
}, []);
const routeChange = () => {
const path = `/${props.product.id.toString()}`;
navigate(path);
};
const displayComparison = (e) => {
props.setComparisonProduct(props.product);
props.setModalShowing(true);
e.stopPropagation();
};
const getAverageRating = (ratings) => {
var sum = 0;
var count = 0;
Object.keys(ratings).forEach(function(rating) {
sum += rating * parseInt(ratings[rating]);
count += parseInt(ratings[rating]);
});
return sum / count;
};
return (
!props.isFetching ?
<>
<div className='product-card-container' onClick={() => routeChange(props.product.id)}>
<img className='product-card-image' src={props.product.styles.results[0].photos[0].thumbnail_url}>
</img>
{props.parentComponent === 'RelatedProducts'
?
<svg className="open-comparison-btn" role='button' aria-label='open comparison' width="20px" height="20px" viewBox="0 0 32 32" onClick={(e) => { displayComparison(e); }}>
<path fill="White" stroke="black" strokeWidth="2px" d="M20.388,10.918L32,12.118l-8.735,7.749L25.914,31.4l-9.893-6.088L6.127,31.4l2.695-11.533L0,12.118
l11.547-1.2L16.026,0.6L20.388,10.918z"/>
</svg>
:
<div className="close-btn" onClick={() => { props.removeFromOutfit(props.product); }}></div>
}
<div className='product-card-description'>
<div className='product-card-category'>{props.product.category}</div>
<div className='product-card-name'>{props.product.name}</div>
<div className='product-card-price'>${props.product.default_price}</div>
<div className='product-card-stars'>{ stars }</div>
</div>
</div>
</>
: null
);
};
export default ProductCard;
Test:
it('tests that clicking the open-comparison-btn opens the modal window', async () => {
render(<RelatedProducts
addToOutfit={() => { return; }}
yourOutfit={() => { return; }}
relatedProducts={relatedProducts}
generateStars={ generateStars }
isFetching={() => { return false; }}
setIsFetching={() => { return; }}
/>, {wrapper: Router});
fireEvent(
screen.getByRole('button', {name: 'open comparison'}),
new MouseEvent('click', {
bubbles: true,
cancelable: true,
}),
);
const modal = screen.getByRole('dialog', {name: 'comparison window'});
expect(modal).toBeInTheDocument();
});
Any advice would be appreciated.
The answer ended up being simply using await before render...
I think it might be because my child component was doing an API call, but I'm not sure why I did not need to use await when rendering and testing the child component separately.

It is necessary that when the component is removed, it does not just disappear, but leaves behind component with button

thank you in advance, however, before answering the question, read carefully what I ask for help with all due respect. What i need:
I need that when the delete button is clicked, the component is not only deleted, but also leaves behind another button, by clicking on which, the remote component is rendered back
Functionality that already works: rendering a component on click, as well as deleting by a button
import React, {useState} from 'react';
import ReactDOM from 'react-dom';
interface IParams {
id: number;
}
interface IBlock {
deleteBlock(blockToDelete: number) : void
id: number
}
function App() {
const [newBlock, setNewBlock] = useState([] as IParams[])
const createOnClick = () => {
const newId = {
id: newBlock.length + 1
}
setNewBlock([...newBlock, newId])
}
const deleteBlock = (blockToDelete: number) => {
setNewBlock(
newBlock.filter((x) => {
return x.id !== blockToDelete
})
)
}
const FunctionalBlock: React.FC<IBlock> = ({id, deleteBlock}) => {
return (
<div style={{display: 'flex', maxWidth: '300px', justifyContent: 'space-between'}}>
<div>i was created {id} times</div>
<button onClick={() => {
deleteBlock(id)
}}>now delete me</button>
</div>
)
}
return (
<div className="App">
<button onClick={createOnClick}>
New block
</button>
{
newBlock.map((x) => (
<FunctionalBlock id={x.id} key={x.id} deleteBlock={deleteBlock}/>
))
}
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Why not just set a state on the block?
const {useState} = React
const FunctionalBlock = ({ id, idx, isDeleted, toggleBlockState }) => {
return (
<div
style={{
display: "flex",
maxWidth: "300px",
justifyContent: "space-between"
}}
>
{
!isDeleted
? <React.Fragment>
<div>i was created {idx} times</div>
<button
onClick={toggleBlockState}
>
now delete me
</button>
</React.Fragment>
: <button onClick={toggleBlockState}>REVIVE BLOCK</button>
}
</div>
)
;
};
const getNewBlock = (idx) => ({
id: Date.now(),
idx,
isDeleted: false,
})
const toggleIsDeletedById = (id, block) => {
if (id !== block.id) return block
return {
...block,
isDeleted: !block.isDeleted
}
}
const App = () => {
const [newBlock, setNewBlock] = useState([])
const createOnClick = () => {
const block = getNewBlock(newBlock.length + 1)
setNewBlock([...newBlock, block])
}
const toggleBlockStateById = (id) => {
setNewBlock(newBlock.map((block) => toggleIsDeletedById(id, block)))
}
return (
<div>
<div>NEW BLOCK</div>
<div><button onClick={createOnClick}>ADD NEW BLOCK +</button></div>
<div>
{
newBlock.map(block => <FunctionalBlock {...block} toggleBlockState={() => toggleBlockStateById(block.id)}/>)
}
</div>
</div>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

While creating item from parent to child component, going to infinite loop in React

This is my home page and getting list data then creating List component as much as need.
Then going to List component and creating list item. so creating new component using map function. I tried different ways for it but I can't figure out. It goes infinite loop whatever I did.
Home.jsx
const [lists, setLists] = useState([]);
useEffect(() => {
const getRandomList = async () => {
try {
const res = await axios.get(`/lists${type ? "?type=" + type : ""}`, {
headers: {
"Content-Type": "application/json;charset=UTF-8",
token: token,
},
});
setLists(res.data);
} catch (error) {
console.log(error);
}
};
getRandomList();
}, [type]);
return (
<div className="home">
<Navbar />
<Featured type={type} />
{lists.map((list, index) => {
return <List key={index} list={list}/>;
})}
</div>
);
}
in List component, creating List item component as much as need.
List.jsx
export default function List({ list }) {
const [isMoved, setIsMoved] = useState(false);
const [slideNumber, setSlideNumber] = useState(0);
const [clickLimit, setClickLimit] = useState(window.innerWidth / 230);
const [lists, setLists] = useState([])
const listRef = useRef();
useEffect( () =>{
const getList = ()=>{
setLists(list.content)
}
getList()
},[list])
const handleClick = (direction) => {
setIsMoved(true);
let distance = listRef.current.getBoundingClientRect().x - 50;
if (direction === "left" && slideNumber > 0) {
setSlideNumber(slideNumber - 1);
listRef.current.style.transform = `translateX(${230 + distance}px)`;
}
if (direction === "right" && slideNumber < 10 - clickLimit) {
setSlideNumber(slideNumber + 1);
listRef.current.style.transform = `translateX(${-230 + distance}px)`;
}
};
return (
<div className="list">
<span className="listTitle">Continue to watch</span>
<div className="listWrapper">
<ArrowBackIosOutlined
className="sliderArrow left"
onClick={() => handleClick("left")}
style={{ display: !isMoved && "none" }}
/>
<div className="listContainer" ref={listRef}>
{lists.map((item, index) => {
return <ListItem key={index} index={index} item={item} />;
})}
</div>
<ArrowForwardIosOutlined
className="sliderArrow right"
onClick={() => handleClick("right")}
/>
</div>
</div>
);
}
last component here.
ListItem.jsx
export default function ListItem({ index, item }) {
const [isHovered, setIsHovered] = useState(false);
const [movie, setMovie] = useState({});
useEffect(() => {
const getMovie = async (item) => {
try {
const res = await axios.get("/movies/find/" + item, {
headers: {
"Content-Type": "application/json;charset=UTF-8",
token: token,
},
});
setMovie(res.data);
} catch (error) {
console.log(error);
}
};
getMovie()
}, [item]);
return (
<div
className="listItem"
style={{
left:
isHovered &&
Object.values(index) * 225 - 50 + Object.values(index) * 2.5,
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<img src={movie.img} alt="" />
{isHovered && (
<>
<video src={movie.trailer} autoPlay loop></video>
<div className="itemInfo">
<div className="itemIcons">
<PlayArrow className="itemIcon" />
<Add className="itemIcon" />
<ThumbUpAltOutlined className="itemIcon" />
<ThumbDownAltOutlined className="itemIcon" />
</div>
<div className="itemInfoTop">
<span>1 hour 14 mins</span>
<span className="limit">{movie.limit}</span>
<span>{movie.year}</span>
</div>
<div className="itemDescription">{movie.description}</div>
<div className="itemGenre">{movie.genre}</div>
</div>
</>
)}
</div>
);
}
Try to remove the useEffect from the List component and map on the list.content directly:
export default function List({ list }) {
const [isMoved, setIsMoved] = useState(false);
const [slideNumber, setSlideNumber] = useState(0);
const [clickLimit, setClickLimit] = useState(window.innerWidth / 230);
const listRef = useRef();
const handleClick = (direction) => {
setIsMoved(true);
let distance = listRef.current.getBoundingClientRect().x - 50;
if (direction === "left" && slideNumber > 0) {
setSlideNumber(slideNumber - 1);
listRef.current.style.transform = `translateX(${230 + distance}px)`;
}
if (direction === "right" && slideNumber < 10 - clickLimit) {
setSlideNumber(slideNumber + 1);
listRef.current.style.transform = `translateX(${-230 + distance}px)`;
}
};
return (
<div className="list">
<span className="listTitle">Continue to watch</span>
<div className="listWrapper">
<ArrowBackIosOutlined
className="sliderArrow left"
onClick={() => handleClick("left")}
style={{ display: !isMoved && "none" }}
/>
<div className="listContainer" ref={listRef}>
{list.content.map((item, index) => {
return <ListItem key={index} index={index} item={item} />;
})}
</div>
<ArrowForwardIosOutlined
className="sliderArrow right"
onClick={() => handleClick("right")}
/>
</div>
</div>
);
}

How to filter array and rerendering results in ReactJS

I'm building a finance stock exchange website, I'm currently trying to filter results according to a select menu and rerendering the results in ReactJs. My filter function is working and find the correct answer but the re-rendering make the whole website crash and I don't know why. If anyone could help I'll be more than glad ;)
import React, { useState, useEffect } from "react";
import { useAuth0 } from "#auth0/auth0-react";
import "../style/content.css";
import LoginButton from "./LoginButton";
import Modal from "./Modal";
var axios = require("axios").default;
const StockContent = () => {
const { isAuthenticated } = useAuth0();
const [datas, setDatas] = useState([]);
const [selectFil, setSelectFil] = useState("");
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
var options = {
method: "GET",
url: "https://run.mocky.io/v3/6daa5c5c-4c1b-4bce-8596-c604fa9f52dc",
params: { modules: "defaultKeyStatistics,assetProfile" }
};
axios
.request(options)
.then(function (response) {
var results = response.data.marketSummaryResponse.result;
setDatas(results);
console.log(results);
})
.catch(function (error) {
console.error(error);
});
}, []);
const handleChange = (e) => {
debugger;
setSelectFil(e.target.value);
};
const findMatch = (selected) => {
debugger;
const matchData = datas.find((data) => data.exchange === selected);
setDatas(matchData);
};
return (
<>
{isAuthenticated ? (
<>
<div className="title">
<h1>
Market Summary <i class="fas fa-chart-pie"></i>
</h1>
</div>
<div className="filter_sec">
<h3>Filter Results</h3>
<br></br>
<select name="exchange" id="stock" value={selectFil} onChange={handleChange}>
{datas.map((data, i) => {
return <option>{data.exchange}</option>;
})}
</select>
<select name="market" id="mark">
{datas.map((data, i) => {
return <option value="market">{data.market}</option>;
})}
</select>
<button onClick={() => findMatch(selectFil)}>Filter</button>
</div>
<div className="card_content">
{datas &&
datas.map((data, i) => {
return (
<div
key={i}
style={{
height: "200px",
width: "600px",
alignItems: "center",
margin: "20px 30px",
borderRadius: "20px",
backgroundColor: "rgba(98, 98, 98, 0.479)"
}}
>
<div className="card_info">
<div className="logo_sn">
<h4 id="stock_sn">{data.shortName}</h4>
<div className="logo"></div>
</div>
<div className="card_info_det">
<h4>Exchange : {data.exchange}</h4>
<h4>Market : {data.market}</h4>
<h4>TimeZone : {data.exchangeTimezoneName}</h4>
<h4>Change Rate : {data.regularMarketChangePercent.fmt}</h4>
<h4>Market Price :{data.regularMarketPrice.fmt} $</h4>
</div>
<div>
<Modal
exchange={data.exchange}
exchangeDelay={data.exchangeDataDelayedBy}
exchangeMilisec={data.firstTradeDateMilliseconds}
marketState={data.marketState}
priceHint={data.priceHint}
quoteSrcN={data.quoteSourceName}
quoteType={data.quoteType}
regMarketChF={data.regularMarketChange.fmt}
regMarketChR={data.regularMarketChange.raw}
regMarketPrevCF={data.regularMarketPreviousClose.fmt}
regMarketPrevCR={data.regularMarketPreviousClose.raw}
regMarketTimeF={data.regularMarketTime.fmt}
regMarketTimeR={data.regularMarketTime.raw}
shortName={data.shortName}
srcInter={data.sourceInterval}
symbol={data.symbol}
tradeable={data.tradeable}
trigger={data.triggerable}
isOpen={isOpen}
onClose={(e) => setIsOpen(false)}
/>
</div>
</div>
</div>
);
})}
</div>
</>
) : (
<div className="not_logged_page">
<h1>Welcome to Finance Stock </h1>
<LoginButton />
</div>
)}
</>
);
};
export default StockContent;
Please add checkpoint to check length of an array
{datas && datas.length > 0 && datas.map((data, i) => { ... continue

Checkbox isChecked doesn't affect the price

I've two different prices,
when i check the checkbox for second price,
the state show and display the second price properly
but when i press the pay button of that product with second price, it will send the first price
instead of what I've checked which was second price.
so i want increment both prices separately when i checked the check box and
it sends the specific price to the payment process
import React, { useState, useEffect } from "react";
import {
getProducts,
getBraintreeClientToken,
processPayment,
createOrder
} from "./apiCore";
import { emptyCart } from "./cartHelpers";
import { isAuthenticated } from "../auth";
import { Link } from "react-router-dom";
import "braintree-web";
import DropIn from "braintree-web-drop-in-react";
import { makeStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
}));
const Checkout = ({ products, setRun = f => f, run = undefined }) => {
const classes = useStyles();
const [data, setData] = useState({
loading: false,
success: false,
clientToken: null,
error: "",
instance: {},
address: "",
mobile:""
});
>>>>>> const [price, setPrice]= useState(() => () => getTotal())
>>>>>> const [isChecked, setIsChecked]= useState(false);
const userId = isAuthenticated() && isAuthenticated().user._id;
const token = isAuthenticated() && isAuthenticated().token;
const getToken = (userId, token) => {
getBraintreeClientToken(userId, token).then(data => {
if (data.error) {
setData({ ...data, error: data.error });
} else {
setData({ clientToken: data.clientToken });
}
});
};
useEffect(() => {
getToken(userId, token);
}, []);
const handleAddress = event => {
setData({ ...data, address: event.target.value });
};
const handleMobile = event => {
setData({ ...data, mobile: event.target.value });
};
>>>>>> const getTotal = () => {
>>>>>> return products.reduce((currentValue, nextValue) => {
>>>>>> return currentValue + nextValue.count * nextValue.price;
>>>>>>
>>>>>> }, 0);
>>>>>> };
>>>>>> const getTotal2 = () => {
>>>>>> return products.reduce((currentValue, nextValue) => {
>>>>>> return currentValue + nextValue.count * nextValue.price2;
>>>>>> }, 0);
>>>>>> };**
const showCheckout = () => {
return isAuthenticated() ? (
<div>{showDropIn()}</div>
) : (
<Link to="/signin">
<button className="btn btn-primary">Sign in to checkout</button>
</Link>
);
};
let deliveryAddress = data.address
let deliveryMobile = data.mobile
const buy = () => {
setData({ loading: true });
// send the nonce to your server
// nonce = data.instance.requestPaymentMethod()
let nonce;
let getNonce = data.instance
.requestPaymentMethod()
.then(data => {
// console.log(data);
nonce = data.nonce;
// once you have nonce (card type, card number) send nonce as 'paymentMethodNonce'
// and also total to be charged
// console.log(
// "send nonce and total to process: ",
// nonce,
// getTotal(products)
// );
>>>>>> **const paymentData = {
>>>>>> paymentMethodNonce: nonce,
>>>>>> amount: getTotal(products)
>>>>>> };**
processPayment(userId, token, paymentData)
.then(response => {
console.log(response);
// empty cart
// create order
const createOrderData = {
products: products,
transaction_id: response.transaction.id,
amount: response.transaction.amount,
address: deliveryAddress,
mobile: deliveryMobile
};
createOrder(userId, token, createOrderData)
.then(response => {
emptyCart(() => {
setRun(!run); // run useEffect in parent Cart
console.log(
"payment success and empty cart"
);
setData({
loading: false,
success: true
});
});
})
.catch(error => {
console.log(error);
setData({ loading: false });
});
})
.catch(error => {
console.log(error);
setData({ loading: false });
});
})
.catch(error => {
// console.log("dropin error: ", error);
setData({ ...data, error: error.message });
});
};
const showDropIn = () => (
<div onBlur={() => setData({ ...data, error: "" })}>
{data.clientToken !== null && products.length > 0 ? (
<div>
<div className="gorm-group mb-3">
<label className="text-muted">Delivery address:</label>
<textarea
onChange={handleAddress}
className="form-control"
value={data.address}
placeholder="Type your delivery address here..."
/>
</div>
<div className="gorm-group mb-3">
<label className="text-muted">mobile number:</label>
<form className={classes.root} noValidate autoComplete="off">
<TextField placeholder="mobile number" onChange={handleMobile} type="text" value={data.mobile} id="outlined-basic" label="mobile" variant="outlined" />
</form>
</div>
<DropIn
options={{
authorization: data.clientToken,
paypal: {
flow: "vault"
}
}}
onInstance={instance => (data.instance = instance)}
/>
<button onClick={buy} className="btn btn-success btn-block">
Pay
</button>
</div>
) : null}
</div>
);
const showError = error => (
<div
className="alert alert-danger"
style={{ display: error ? "" : "none" }}
>
{error}
</div>
);
const showSuccess = success => (
<div
className="alert alert-info"
style={{ display: success ? "" : "none" }}
>
Thanks! Your payment was successful!
</div>
);
const showLoading = loading =>
loading && <h2 className="text-danger">Loading...</h2>;
return (
<div>
>>>>>> **<form>
>>>>>> <h1>قیمت کل: {isChecked ? getTotal2() : getTotal()} </h1>
>>>>>> <label>نیم لیتر :</label>
>>>>> <input type="checkbox"
>>>>>> checked={isChecked}
>>>>>> onChange={(e)=>{setIsChecked(e.target.checked)}}/>
>>>>>> </form>**
{showLoading(data.loading)}
{showSuccess(data.success)}
{showError(data.error)}
{showCheckout()}
</div>
);
};
export default Checkout;
my card component
import React, { useState, version,useEffect } from 'react';
import { Link, Redirect } from 'react-router-dom';
import ShowImage from './ShowImage';
import moment from 'moment';
import { addItem, updateItem, removeItem } from './cartHelpers';
import logo from '../images/logo.svg'
//material ui
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid'
import { Typography } from '#material-ui/core';
import CardHeader from '#material-ui/core/CardHeader';
import CardMedia from '#material-ui/core/CardMedia';
import CardActions from '#material-ui/core/CardActions';
import Collapse from '#material-ui/core/Collapse';
import Avatar from '#material-ui/core/Avatar';
import IconButton from '#material-ui/core/IconButton';
import MoreVertIcon from '#material-ui/icons/MoreVert';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import { create } from 'jss';
import rtl from 'jss-rtl';
import { StylesProvider, jssPreset } from '#material-ui/core/styles';
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const useStyles = makeStyles((theme) => ({
stock: {
marginBottom: '1rem'
},
Button: {
...theme.typography.button,
padding: '.3rem',
minWidth: 10,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
title: {
backgroundColor: '#D8D8D8'
},
grid: {
padding: '0'
},
cardFont:{
...theme.typography.body,
textAlign:'right',
marginTop:'.8rem',
marginRight:'1rem'
},productName:{
...theme.typography.body,
textAlign:'right',
},
cardButton:{
marginLeft:'1.4rem'
}
}));
const Cardd = ({
product,
showViewProductButton = true,
showAddToCartButton = true,
cartUpdate = false,
showRemoveProductButton = false,
setRun = f => f,
run = undefined
// changeCartSize
}) => {
const [redirect, setRedirect] = useState(false);
const [count, setCount] = useState(product.count);
//material ui
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
// console.log(price)
const classes = useStyles()
const showViewButton = showViewProductButton => {
return (
showViewProductButton && (
<Link to={`/product/${product._id}`} className="mr-2">
<Button className={classes.Button} size="small" variant="contained">مشاهده محصول</Button>
</Link>
)
);
};
const addToCart = () => {
// console.log('added');
addItem(product, setRedirect(true));
};
const shouldRedirect = redirect => {
if (redirect) {
return <Redirect to="/cart" />;
}
};
const showAddToCartBtn = showAddToCartButton => {
return (
showAddToCartButton && (
<Button className={classes.Button} size="small" onClick={addToCart} variant="contained" color="primary" disableElevation>
اضافه کردن به سبد
</Button>
)
);
};
const showStock = quantity => {
return quantity > 0 ? (
<Button disabled size="small" className={classes.stock}>موجود </Button>
) : (
<Button className={classes.stock}>ناموجود </Button>
);
};
const handleChange = productId => event => {
setRun(!run); // run useEffect in parent Cart
setCount(event.target.value < 1 ? 1 : event.target.value);
if (event.target.value >= 1) {
updateItem(productId, event.target.value);
}
};
const showCartUpdateOptions = cartUpdate => {
return (
cartUpdate && (
<div>
<div className="input-group mb-3">
<div className="input-group-prepend">
<span className="input-group-text">تعداد</span>
</div>
<input type="number" className="form-control" value={count} onChange={handleChange(product._id)} />
</div>
</div>
)
);
};
const showRemoveButton = showRemoveProductButton => {
return (
showRemoveProductButton && (
<Button className={classes.Button} size="small" variant="contained" color="secondary" disableElevation
onClick={() => {
removeItem(product._id);
setRun(!run); // run useEffect in parent Cart
}}
>
حذف محصول
</Button>
)
);
};
return (
<Grid container direction='column' >
<Grid item >
<Card style={{margin:'1rem'}}>
<Grid item className={classes.title}>
<Typography className={classes.productName} variant='h6'>
{product.name}
</Typography>
</Grid>
<CardHeader
avatar={
<Avatar alt="Remy Sharp" src={logo} className={classes.green}>
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title="روغنک"
subheader="September 14, 2016"
/>
<CardContent className={classes.grid} >
{shouldRedirect(redirect)}
<ShowImage item={product} url="product" />
<StylesProvider jss={jss}>
<Typography className={classes.cardFont} variant="body2" component="p">
{product.description.substring(0, 100)}
</Typography>
</StylesProvider>
<div>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
<Typography className={classes.cardFont} variant="body2" component="p">
$یک لیتر {product.price}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
$نیم لیتر {product.price2}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
Category: {product.category && product.category.name}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
Added on {moment(product.createdAt).fromNow()}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
{showStock(product.quantity)}
</Typography>
<br />
<Grid container >
<Grid item className={classes.cardButton} >
{showViewButton(showViewProductButton)}
</Grid>
<Grid item className={classes.cardButton}>
{showAddToCartBtn(showAddToCartButton)}
</Grid>
<Grid item className={classes.cardButton}>
{showRemoveButton(showRemoveProductButton)}
</Grid>
</Grid>
{showCartUpdateOptions(cartUpdate)}
</CardContent>
</Card>
</Grid>
</Grid>
// <div className="card ">
// <div className="card-header card-header-1 ">{product.name}</div>
// <div className="card-body">
// {shouldRedirect(redirect)}
// <ShowImage item={product} url="product" />
// <p className="card-p mt-2">{product.description.substring(0, 100)} </p>
// <p className="card-p black-10">$ {product.price}</p>
// <p className="black-9">Category: {product.category && product.category.name}</p>
// <p className="black-8">Added on {moment(product.createdAt).fromNow()}</p>
// {showStock(product.quantity)}
// <br />
// {showViewButton(showViewProductButton)}
// {showAddToCartBtn(showAddToCartButton)}
// {showRemoveButton(showRemoveProductButton)}
// {showCartUpdateOptions(cartUpdate)}
// </div>
// </div>
);
};
export default Cardd;
CartHelper component
export const addItem = (item = [], count = 0, next = f => f) => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.push({
...item,
count: 1
});
// remove duplicates
// build an Array from new Set and turn it back into array using Array.from
// so that later we can re-map it
// new set will only allow unique values in it
// so pass the ids of each object/product
// If the loop tries to add the same value again, it'll get ignored
// ...with the array of ids we got on when first map() was used
// run map() on it again and return the actual product from the cart
cart = Array.from(new Set(cart.map(p => p._id))).map(id => {
return cart.find(p => p._id === id);
});
localStorage.setItem('cart', JSON.stringify(cart));
next();
}
};
export const itemTotal = () => {
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
return JSON.parse(localStorage.getItem('cart')).length;
}
}
return 0;
};
export const getCart = () => {
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
return JSON.parse(localStorage.getItem('cart'));
}
}
return [];
};
export const updateItem = (productId, count) => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.map((product, i) => {
if (product._id === productId) {
cart[i].count = count;
}
});
localStorage.setItem('cart', JSON.stringify(cart));
}
};
export const removeItem = productId => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.map((product, i) => {
if (product._id === productId) {
cart.splice(i, 1);
}
});
localStorage.setItem('cart', JSON.stringify(cart));
}
return cart;
};
export const emptyCart = next => {
if (typeof window !== 'undefined') {
localStorage.removeItem('cart');
next();
}
};

Categories