How can i reduce raiting to be able to rate only once per browser?
I need to make validation for raiting component in React where client can rate only once per browser.
Here is example of the rating component:
export default function App() {
const [value, setValue] = React.useState(0);
return (
<div>
<Box component="fieldset" mb={3} borderColor="transparent">
<Typography component="legend">Rate</Typography>
<Rating
name="pristine"
value={value}
onClick={(e) => setValue(e.target.value)}
/>
</Box>
</div>
);
}
Here is sandbox link of the component sandbox link
One possible solution you could think of is to use localStorage
For every user, who has already rate, you then store a boolean to the local storage.
Then next time, it will be validated first
Something like this:
export default function App() {
const [value, setValue] = React.useState(0);
const hasRated = localStorage.getItem("hasRated");
const handleRate = (e) => {
setValue(e.target.value);
localStorage.setItem("hasRated", true);
};
return (
<div>
<Box component="fieldset" mb={3} borderColor="transparent">
<Typography component="legend">Rate</Typography>
<Rating
disabled={hasRated}
name="pristine"
value={value}
onClick={handleRate}
/>
</Box>
</div>
);
}
Please note, that this is only a demonstrated example, so it supposes that you will need to have some improvement based on your needs
Sanbox Example:
Related
Okay there is definitely a quick solution for this I just can't figure out.
Just a description of what I am trying to do:
Whenever I hover over a certain card, I would like to see the description of that item and only that item. But instead what's obviously happening, as you can see from my code, is every single cards description is showing.
I rewrote a simpler version of the code by taking out any unnecessary pieces. Everything is imported correctly, styling and classNames were removed as well.
export function Items() {
const [items, setItems] = useState([])
const [isHovering, setIsHovering] = useState(false)
useEffect(() => {
setItems(Data)
}, [])
function handleMouseOver() {
setIsHovering(true)
}
function handleMouseOut() {
setIsHovering(false)
}
return(
<div>
{items.map(item => {
return(
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} key={item.id}>
{isHovering ?
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
:
<Card.Body>
</Card.Body>
}
<Card.Footer>
</Card.Footer>
</Card>
)
})}
</div>
)
}
As far as I can see you don't need to put this logic into parent component, and also it makes everything more complex, since it's hard to manage hovering. I would create new chlid component and manage this state out there internally.
export function Item({item}) {
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
setItems(Data);
}, []);
function handleMouseOver() {
setIsHovering(true);
}
function handleMouseOut() {
setIsHovering(false);
}
return (
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
{isHovering ? (
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
) : (
<Card.Body></Card.Body>
)}
<Card.Footer></Card.Footer>
</Card>
);
}
export function Items() {
const [items, setItems] = useState([]);
return (
<div>
{items.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
Your "isHovering" state should also be an array, where you store the hover state for every card. Then on hover set "isHovering" to true only for the right card.
I have a datagrid table in which I'm getting my database data from an API call and I have written the table code in one file. I also have a search functionality where you can search for a particular record inside the table, but this search code is in another file. I am having difficulty of passing my state variable containing the search parameter from my search file to the table file. I have separated all my components in different pages since it'd be easier to structure them using a grid in my App.js. How do I get my search query to my table file next?
My search code:
export default function SearchInput() {
const [searchTerm, setSearchTerm] = React.useState('');
return (
<Grid item xs={3}>
<Box mt={1.6}
component="form"
sx={{
'& > :not(style)': { m: 1, width: '20ch', backgroundColor: "white", borderRadius: 1},
}}
noValidate
autoComplete="off"
>
<TextField
placeholder="Search Customer ID"
variant="outlined"
size="small"
sx={{input: {textAlign: "left"}}}
onChange={(event) => {
setSearchTerm(event.target.value);
console.log(searchTerm);
}}
/>
</Box>
</Grid>
);
}
My table code:
export default function DataTable() {
const [pageSize, setPageSize] = React.useState(10);
const [data, setData] = React.useState([]);
useEffect(async () => {
setData(await getData());
}, [])
return (
<div style={{ width: '100%' }}>
<DataGrid
rows={data}
columns={columns}
checkboxSelection={true}
autoHeight={true}
density='compact'
rowHeight='40'
headerHeight={80}
disableColumnMenu={true}
disableSelectionOnClick={true}
sx={datagridSx}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
rowsPerPageOptions={[5, 10, 15]}
pagination
/>
</div>
);
}
App.js
function App() {
return (
<div className="App">
<Container maxWidth="false" disableGutters="true">
<Grid container spacing={0}>
<ABClogo />
<HHHlogo />
</Grid>
<Grid container spacing={0}>
<LeftButtonGroup />
<SearchInput />
<RightButtonGroup />
</Grid>
<Grid container spacing={0}>
<DataTable />
<TableFooter />
</Grid>
</Container>
</div>
);
}
Here is a minimal example using createContext(), and useReducer() to lift up state and share it between components, similar to what you are after, but as jsNoob says, there are multiple options. This is one I'm comfortable with.
The concept is explained here: https://reactjs.org/docs/context.html
Essentially you can create 'global' state at any point in your component tree and using Provider / Consumer components, share that state and functionality with child components.
//Main.js
import React, { createContext, useContext, useReducer } from 'react';
const MainContext = createContext();
export const useMainContext => {
return useContext(MainContext);
}
const mainReducer = (state, action) => {
switch(action.type){
case: 'SOMETHING':{
return({...state, something: action.data});
}
default:
return state;
}
}
export const Main = () => {
const [mainState, mainDispatch] = useReducer(mainReducer, {something: false});
const stateOfMain = { mainState, mainDispatch };
return(
<MainContext.Provider value={stateOfMain}>
<MainContext.Consumer>
{() => (
<div>
<Nothing />
<Whatever />
</div>
)}
</MainContext.Consumer>
</MainContext.Provider>
)
}
Then you can have the other components use either or both of the main state and dispatch.
//Nothing.js
import {mainContext} from './Main.js'
const Nothing = () => {
const { mainState, mainDispatch } = useMainContext();
return(
<button onClick={() => {mainDispatch({type: 'SOMETHING', data: !mainState.something})}}></button>
)
}
Clicking the button in the above file, should change the display of the below file
//Whatever.js
import {mainContext} from './Main.js'
const Whatever = () => {
const { mainState } = useMainContext();
return(
<div>{mainState.something}</div>
);
}
I am trying to figure out why any event triggers a visible page refresh when re-rendering. The following is the parent component :
export default function FullScreenDialog({purchaseList={"testtest":"900","heyzi":"90"}}) {
const [open, setOpen] = React.useState(false);
const [pieChartData,setPieChartData]=React.useState([])
const [formset,setFormset]=React.useState([
{
id: uuidv4(),
product:"",
price: 0,
quantity: 0,
productSubtotal: 0,
}
])
const singleForm= {
id: uuidv4(),
product:"",
price: 0,
quantity: 0,
productSubtotal: 0,
}
const handleProduct = (e,id) => {
const newFormset=formset.map(item=>
(item.id !== id? item : {
...item , product: e.target.value , price:purchaseList[e.target.value]
})
)
setFormset(newFormset)
}
const handleQuantity = (e,id) => {
const newFormset=formset.map((item)=>
{
if (item.id===id){
const newItem={
...item, quantity: e.target.value
}
return newItem
}
return item
})
setFormset(newFormset)
}
const handleAdd=()=>{
setFormset([...formset,singleForm])
}
const handleDelete=(id)=>{
const newFormset=formset.filter(item=>
item.id !==id
)
setFormset(newFormset)
}
//below is solely from this component, up is the state from children
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
handlePieChartData()
};
const handlePieChartData=()=>{
setPieChartData(
[...pieChartData,{id:uuidv4(), data:formset }])
}
console.log(formset)
console.log(pieChartData)
return (
<div>
<ListOfPieChartsPresenter
open={open}
handleClickOpen={handleClickOpen}
handleClose={handleClose}
handleProduct={handleProduct}
handleQuantity={handleQuantity}
handleDelete={handleDelete}
handleAdd={handleAdd}
purchaseList={purchaseList}
formset={formset}/>
</div>
);
}
Here is the ListOfPieChartsPresenter component:
const ListOfPieChartsPresenter=({handleClickOpen,open,handleClose,handleProduct,handleQuantity,handleDelete,handleAdd,purchaseList,formset})=>{
const classes = useStyles();
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open full-screen dialog
</Button>
<Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton edge="start" color="inherit" onClick={handleClose} aria-label="close">
{/* <CloseIcon /> */}
</IconButton>
<Typography variant="h6" className={classes.title}>
Sound
</Typography>
<Button autoFocus color="inherit" onClick={handleClose}>
save
</Button>
</Toolbar>
</AppBar>
<form >
<PieChartGroupForm
handleProduct={handleProduct}
handleQuantity={handleQuantity}
handleDelete={handleDelete}
handleAdd={handleAdd}
purchaseList={purchaseList}
formset={formset} />
</form>
</Dialog>
</div>
)
}
Lastly, this is the PieChartGroupFormPresenter:
const PieChartGroupFormPresenter=({handleProduct,handleQuantity,handleDelete,product,quantity,item,purchaseList,id})=>{
return (
<div>
<FormControl>
<Select onChange={(e)=>handleProduct(e,id)} value={product}>
{Object.keys(purchaseList).map((item,index) =>
<MenuItem value={item} key={index}>{item}</MenuItem>
)}
</Select>
<TextField onChange={(e)=>handleQuantity(e,id)} value={quantity} />
<TextField value={item.price} disabled>{item.price}</TextField>
<button onClick={()=>handleDelete(id)}>Delete</button>
</FormControl>
<br>
</br>
</div>
)
}
I have been reviewing this for approx an hour now and I still have no idea why this is happening. Could you please share your opinion? Thank you !
The first issue is that, with function components, every time the parent rerenders, the child will also rerender. You can fix this via React.memo:
const PieChartGroupFormPresenter = React.memo(({handleProduct,handleQuantity,handleDelete,product,quantity,item,purchaseList,id})=>{
//...
});
The second issue is that every time you execute code like const handleAdd=()=>{...} you get a different function (closure). So each time FullScreenDialog's state changes, it's going to change all the props passed to ListOfPieChartsPresenter.
You can use the useCallback hook to improve this: then the function/closure will change only when the specified dependencies change. For example:
const handleAdd = React.useCallback(()=>{
setFormset([...formset,singleForm])
}, [formset, singleForm]);
However, note that this callback function will still change whenever formset or singleForm change, so it will still cause a rerender when this state changes.
So hopefully it's at least clear why rerenders are happening, and why they are difficult to prevent. Unfortunately, I'm not familiar enough with React transitions to figure out why they're triggering for each rerender.
I have two components that I'm rendering based on the condition of a state, but I'm running into a problem where the wrong component is displayed a split second before the right component is displayed.
Fetching data async:
const [test, setTest] = useState();
const [loading, setLoading] = useState();
const [error, setError] = useState();
const fetchData = async () => {
console.log("running");
setLoading(true);
setError(false);
try {
const result = await axios(
"https://jsonplaceholder.typicode.com/posts/props.selectedId" // Is dynamic and changes on user click
);
setTest(result.data);
} catch (error) {
if (error.response.status == 404) {
setError(error);
setTest(null);
}
}
setLoading(false);
};
Rendering:
return (
<div className={classes.root}>
{!loading && !error && test? (
<div>
<Card className={classes.card}>
<CardContent>
<Title>Adattributes</Title>
<Typography variant="h6" component="h1">
name
</Typography>
<Grid container spacing={3}>
<Grid item xs={3}>
<Typography variant="subtitle2" component="h1">
address
</Typography>
{test.title}
</Grid>
</Grid>
</CardContent>
<CardActions>
<Component1
value={test}
setTest={setTest}
/>
</CardActions>
</Card>
</div>
) : (
<Component2 setTest={setTest} />
)}
</div>
);
});
Am I doing something wrong with the conditional rendering? Or do it have something to do with fetching async?
The initial state of your test state is falsy. This, plus operator precedence can lead to wrong errors. See http://www-lia.deis.unibo.it/materiale/JS/developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence.html.
Probably what you want to so is:
(!loading && !error && test) ? ... : ...
In the first line you use useState hook without initial state, so your state become undefined (remember that undefined is falsy value).
const [test, setTest] = useState();
Either you should set initial state for test, or change your condition for rendering component.
I'm trying to mask a FilledInput Material-ui component to display as currency using this package: https://www.npmjs.com/package/react-currency-format
I tried many different approaches, but none works right. I created a codesandbox with the problem: https://codesandbox.io/s/fancy-silence-vjkxq
Right now I'm implementing this way:
function Amount({ handleChange, values, t }) {
return (
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="amount">{t("Send")}</InputLabel>
<FilledInput
name="amount"
onChange={handleChange}
value={values.amount}
startAdornment={
<InputAdornment position="start">€</InputAdornment>
}
/>
</FormControl>
);
}
export default function ExchangeRateProvider() {
return (
<CurrencyFormat
customInput={Amount}
handleChange={handleAmount}
t={t}
value={values.amount}
thousandSeparator={true}
/>
)
}
The code works fine, it updates the numbers and the material-ui component is applied, however the mask doesn't work.
Thanks in advance
Something like this?
CODESANDBOX
HERE
function App() {
const [value, setValue] = useState("0");
const handleChange = event => {
const { value } = event.target;
setValue(value);
};
return (
<div className="App">
<CurrencyFormat
value={value}
onChange={handleChange}
displayType={"input"}
customInput={FilledInput}
thousandSeparator={true}
startAdornment={<InputAdornment position="start">€</InputAdornment>}
/>
</div>
);
}