I have a goUp and goDown functions that doesn't work in a .jsx file. There's a textarea and two buttons (up and down). Using the buttons the functions should scroll up and scroll down in the textarea. I tested the same code in a .js file and they worked fine. However in a .jsx file they didn't work at all. I did some research, but I couldn't figure out the problem.
import { useState } from 'react'
import Dialog from '#mui/material/Dialog'
import Button from '#mui/material/Button'
import CircularProgress from '#mui/material/CircularProgress'
import CloseIcon from '#mui/icons-material/Close'
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown'
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp'
import { styled } from '#mui/material/styles'
import { UpdateBookModalForm } from './updateBookModalSkin'
import { updateBook } from 'api/book'
import Toast from 'shared/Toast'
import { queryClient } from 'App'
import AppContainer from '../../appSkin.js'
const UpdateBookModal = ({
book_id,
book_title,
book_author,
book_issn,
book_note,
handleClose,
modalOpen,
}) => {
const okayy = document.getElementById("textt");
const [bookTitle, setBookTitle] = useState()
const [author, setAuthor] = useState()
const [issn, setIssn] = useState()
const [note, setNote] = useState()
const [bookUpdating, setBookUpdating] = useState(false)
const [toastMessage, setToastMessage] = useState('')
const [toastType, setToastType] = useState('')
const [toastOpen, setToastOpen] = useState(false)
const goUp = (id) => {
if (id.scrollTop !== 0) {
id.scrollTo({
top: id.scrollTop - 10,
left: 0,
behavior: "smooth"
});
}
};
const goDown = (id) => {
id.scrollTo({
top: id.scrollTop + 10,
left: 0,
behavior: "smooth"
});
};
const handleSubmit = async ev => {
ev.preventDefault()
setBookUpdating(true)
const result = await updateBook(book_id, {
book_title: bookTitle,
book_author: author,
book_issnisbn: issn,
book_notes: note,
})
setBookUpdating(false)
handleClose()
setToastOpen(true)
if (result.error) {
setToastType('error')
if (result.error.response) {
setToastMessage(result.error.response.data.message)
} else if (result.error.request) {
setToastMessage(result.error.request)
} else {
setToastMessage(result.error.message)
}
} else {
setToastType('success')
setToastMessage('Book updated successfully!')
queryClient.invalidateQueries('books')
}
}
const toastClose = (event, reason) => {
if (reason === 'clickaway') {
return
}
setToastOpen(false)
}
return (
<BootstrapDialog
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={modalOpen}
>
<UpdateBookModalForm onSubmit={handleSubmit}>
<AppContainer>
<div className="modal_top">
<input
style={{ lineHeight: 2.5, /*paddingLeft: 15*/ }}
className="h8 input-box font"
type="text"
placeholder="TITLE"
value={bookTitle}
onChange={ev => setBookTitle(ev.target.value)}
/>
<span className="close-icon" onClick={handleClose}>
<CloseIcon />
</span>
</div>
<div className="top" onClick={() => goUp(okayy)}>
<KeyboardArrowUpIcon style={{ cursor: 'pointer' }} />
</div>
<textarea
id="textt"
rows="4"
cols="50"
maxLength="400"
className="h8 text-box font"
placeholder="YOUR NOTES"
value={note}
onChange={ev => setNote(ev.target.value)}
/>
<div className="bottom" onClick={() => goDown(okayy)}>
<KeyboardArrowDownIcon style={{ cursor: 'pointer' }} />
</div>
<div className="modal_bottom">
<div className='note_buttons_container'>
<div className="h7 note">Please note that in most cases we can retrieve <br /> Metadata directly from the uploaded file</div>
<div className="buttons">
<Button
onClick={handleClose}
variant="contained"
className="h6 login-btn font"
>
Cancel
</Button>
<Button
type="submit"
variant="contained"
className="h6 login-btn"
disabled={bookUpdating}
>
{bookUpdating ? (
<CircularProgress
style={{ height: '28px', width: '28px' }}
color="inherit"
/>
) : (
'Save'
)}
</Button>
</div>
</div>
</div>
</AppContainer>
</UpdateBookModalForm>
<Toast
toastClose={toastClose}
open={toastOpen}
severity={toastType}
message={toastMessage}
/>
</BootstrapDialog>
)
}
export default UpdateBookModal
You need to use hook useRef:
const ref = useRef<HTMLTextAreaElement>();
const handleDown = () => {
if (ref.current) {
ref.current.scrollTo({
top: ref.current.scrollTop + 10,
left: 0,
behavior: "smooth"
});
}
};
// .....
<textarea ref={ref} {..otherProps} />
Here is a live example:
Related
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>
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
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>
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();
}
};
So, I am trying to add some data to two different array in react typescript
const [deviceNames, setDeviceNames] = useState<Array<string>>([])
const [serialNumbers, setSerialNumbers] = useState<Array<string>>([])
I am now looping over both the array here and displaying the content
{deviceNames.length > 0 &&
serialNumbers.length > 0 &&
deviceNames.map(deviceName => {
return serialNumbers.map(serialNumber => {
return (
<CardDevice
deviceName={deviceName}
serialNumber={serialNumber}
/>
)
})
})}
I am adding data to these array by clicking on a button and then showing modal and then like this
onSubmit = (values: any) => {
clearError()
setAddDevice(false)
setDeviceNames(deviceName => [...deviceName, values.deviceName])
setSerialNumbers(serialNumber => [...serialNumber, values.serialNumber])
}
I am using react hook form.
So what i want is whenever i loop over both the arrays, each time it should display the content which was just added in the array the new one not the last one again which was already added and displayed. I hope i am able to make some point here. It does the job but whenever user enters new device after adding one, it add the old one again and then the new one and then again and then again same thing.
i just want to display just one new item which was just last added to an array by the user.
Thanks
Basically i answered my own question:
import React, { useState } from "react";
function App() {
const [addCardData, setAddCardData] = useState("");
const [addCards, setAddCards] = useState<Array<string>>([]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setAddCardData(event.target.value);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
setAddCards(prevState => [...prevState, addCardData]);
};
return (
<div
className="App"
style={{ textAlign: "center", margin: "0 auto", marginTop: "10em" }}
>
<form onSubmit={handleSubmit}>
<input type="text" onChange={handleChange} placeholder="Add any text" />
<button type="submit">Add</button>
</form>
{addCards.map(addCard => (
<h3>{addCard}</h3>
))}
</div>
);
}
export default App;
Here is some more Dynamic Approach:
import React, { useState } from "react";
import { TextInput } from "./components/TextInput";
interface Device {
deviceName: string;
serialNumber: string | number;
}
const App: React.FC = () => {
const [deviceName, setDeviceName] = useState("");
const [serialNumber, setSerialNumber] = useState("");
const [deviceInfos, setDeviceInfos] = useState<Device[]>([]);
const handleDeviceChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setDeviceName(event.target.value);
};
const handleSerialChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSerialNumber(event.target.value);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
addDevice();
setDeviceName("");
setSerialNumber("");
};
const addDevice = () => {
const newDevice: Device[] = [
...deviceInfos,
{ deviceName: deviceName, serialNumber: serialNumber }
];
setDeviceInfos(newDevice);
};
return (
<div
className="App"
style={{ textAlign: "center", margin: "0 auto", marginTop: "10em" }}
>
<form onSubmit={handleSubmit}>
<TextInput
type="text"
placeholder="Add Device Name"
handleChange={handleDeviceChange}
value={deviceName}
/>
<TextInput
type="text"
placeholder="Add fuck"
handleChange={handleSerialChange}
value={serialNumber}
/>
<button type="submit">Add</button>
</form>
{deviceInfos.map((device, i) => (
<div key={i}>
<h3>{device.deviceName}</h3>
<h3>{device.serialNumber}</h3>
</div>
))}
</div>
);
};
export default App;
Much Better Approach i used in the production
const ProfileDevices: React.FC<Props> = ({ onSubmit }) => {
const [addDevice, setAddDevice] = useState(false)
const [deviceInfos, setDeviceInfos] = useState<Device[]>([])
const { register, handleSubmit, errors, clearError } = useForm({
mode: 'onSubmit',
})
const addDevices: any = () => {
setAddDevice(true)
}
onSubmit = (values: any) => {
clearError()
setAddDevice(false)
const newDevice: Device[] = [
...deviceInfos,
{ deviceName: values.deviceName, serialNumber: values.serialNumber },
]
setDeviceInfos(newDevice)
}
return (
<ProfileContentContainer>
<ProfileHeader>
<ProfileTitle>Devices</ProfileTitle>
<ProfileActions>
<Button
type="button"
bgType="fill"
size="default"
label="Add Device"
onClick={addDevices}
/>
</ProfileActions>
</ProfileHeader>
{console.log(deviceInfos)}
<DeviceList>
<CardDevice deviceName="Device Name" serialNumber="QR10001123456788" />
<CardDevice deviceName="Device Name" serialNumber="QR10001123456789" />
{deviceInfos.map((device, i) => (
<CardDevice
key={i}
deviceName={device.deviceName}
serialNumber={device.serialNumber}
/>
))}
</DeviceList>
<Modal isActive={addDevice} toggleModal={() => setAddDevice(false)}>
<ModalContent>
<ModalHeader title="Add Device" />
<AuthForm
onSubmit={handleSubmit(onSubmit)}
className="modalAddDeviceForm"
>
<InputText
placeholder="DeviceName"
name="deviceName"
type="text"
register={register({ required: true, maxLength: 10 })}
hasError={errors.deviceName}
errorMessage={
errors.serialNumber ? errors.serialNumber.message : undefined
}
/>
<InputText
placeholder="Serial Number"
name="serialNumber"
type="text"
register={register({ required: true, maxLength: 10 })}
hasError={errors.serialNumber}
errorMessage={
errors.serialNumber ? errors.serialNumber.message : undefined
}
/>
<Button type="submit" bgType="fill" size="default" label="Add" />
</AuthForm>
<ModalActions></ModalActions>
</ModalContent>
</Modal>
</ProfileContentContainer>
)
}
export default ProfileDevices