I have the following component:
import * as React from "react";
import { createTheme, ThemeProvider } from '#mui/material/styles'
import { Box } from '#mui/system';
import { InputBase, TextField, Typography } from "#mui/material";
import ReactQuill from 'react-quill';
import { NoEncryption } from "#mui/icons-material";
type Props = {
issueId: string
}
export default function DialogBox({ issueId }: Props) {
const myTheme = createTheme({
// Set up your custom MUI theme here
})
const [newMsg, setNewMsg] = React.useState("");
const [startNewMsg, setStartNewMsg] = React.useState(false)
const handleNewMsgInput = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
}
const handleKeyPress = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "Escape") {
// setStartNewMsg(false);
setNewMsg((prev) => "");
}
}
return (
<Box flexDirection="column" sx={{ display: "flex", alignItems: "center", backgroundColor: "lightblue", height: "100%", gap: "2rem" }} onKeyPress={(e) => {
handleKeyPress(e)
}}>
<Typography
sx={{
width: "fit-content",
height: "fit-content",
fontFamily: "Amatic SC",
background: "lightblue",
fontSize: "3rem"
}}>
Message board
</Typography>
{startNewMsg ?
<Box sx={{ width: "fit-content", height: "fit-content" }}>
<ReactQuill style={{ backgroundColor: "white", height: "10rem", maxWidth: "30rem", maxHeight: "10rem" }} theme="snow" />
</Box>
:
<TextField id="filled-basic" label="write new message" sx={{ "& fieldset": { border: 'none' }, backgroundColor: "white", borderRadius: "5px" }} variant="filled" fullWidth={true} onClick={(e) => setStartNewMsg((prev) => true)} onChange={(e) => handleNewMsgInput(e)} />}
</Box >
)
}
Which's causing me the following issue of text appearing out of my white textbox:
I notice on inspection the following property which's responsible for the problem:
My question is, what would be the best way to manipulating the values of that element? How should I retrieve them?
Regards!
Use ch units (the size of the text characters) and adjust the size based on the input.
Example:
const Test = () => {
const [text, setText] = useState('');
return (
<div>
<input
value={text}
onChange= {(e) => setText(e.target.value)}
style= {{height: `${text.length}ch`, width: `${text.length}ch`}}
/>
</div>
);
};
^ that is an input box that will grow based on the size of the text.
You don't want:
height: 100%;
as this will only allow the height to be as big as its parent container.
Related
I'm new to coding, and trying to build a social media application. I'm using react js in which I'm not able to provide styling to the Google Login Button. I have tried to provide style in various ways like from tweking the parent component to doing changes in style js, but none of it seems to work.
This is Auth.js
import React, { useState, useEffect } from 'react';
import { Avatar, Button, Paper, Grid, Typography, Container, TextField } from '#material-ui/core';
import { GoogleOAuthProvider, GoogleLogin, googleLogout } from '#react-oauth/google';
// import {GoogleLogin, googleLogout} from 'react-google-login'
import Icon from './icon';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import useStyles from './styles';
import Input from './Input';
const Auth = () => {
const classes = useStyles();
const [showPassword, setShowPassword] = useState(false);
const [isSignup, setIsSignup] = useState(false);
const handleShowPassword = () => setShowPassword((prevShowPassword) => !prevShowPassword);
const handleSubmit = () => {
};
const handleChange = () => {
}
const switchMode = () => {
setIsSignup((prevIsSignup) => !prevIsSignup);
handleShowPassword(false);
}
function handleCallbackResponse(response) {
console.log("JWT ID Token" + response.credential);
}
useEffect(() => {
/*global google*/
google.accounts.id.initialize({
client_id: '29955464017-7ng0cb3718pnb4lmmhd9tqqf1qqgqn4d.apps.googleusercontent.com',
callback: handleCallbackResponse
});
google.accounts.id.renderButton(
document.getElementById('signInDiv'),
{ theme: 'outline', size: 'large' }
)
}, [])
// const googleSuccess = (res) => {
// console.log(res);
// }
// const googleFailure = () => {
// console.log('Google Sign In was unsuccessful. Try again later');
// }
return (
<Container component='main' maxWidth='xs'>
<Paper className={classes.paper} elevation={3}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography variant='h5'>{isSignup ? "Sign Up" : "Sign In"}</Typography>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
{isSignup && (
<>
<Input name='firstName' label='First Name' handleChange={handleChange} autoFocus half />
<Input name='firstName' label='First Name' handleChange={handleChange} half />
</>
)}
<Input name='email' label='Email address' handleChange={handleChange} type='email' />
<Input name='password' label='Password' handleChange={handleChange} type={showPassword ? 'text' : 'password'} handleShowPassword={handleShowPassword} />
{isSignup && <Input name='confirmPassword' label='Repeat Password' handleChange={handleChange} type='password' />}
</Grid>
<Button type='submit' fullWidth variant='contained' color='primary' className={classes.submit}>
{isSignup ? "Sign Up" : "Sign In"}
</Button>
<div id='signInDiv'></div>
<Grid container justifyContent='flex-end'>
<Grid item>
<Button onClick={switchMode}>
{isSignup ? 'Already Have an account? Sign In' : "Don't have an account? Sign Up"}
</Button>
</Grid>
</Grid>
</form>
</Paper>
</Container>
)
}
export default Auth
This is Style.js
import { makeStyles } from '#material-ui/core/styles';
import Icon from './icon';
export default makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: theme.spacing(2),
},
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
googleButton: {
marginBottom: theme.spacing(2),
},
}));
import { makeStyles } from '#material-ui/core/styles';
import Icon from './icon';
export default makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: theme.spacing(2),
},
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
googleButton: {
marginBottom: theme.spacing(2),
},
}));
Please help me understand how can I style the div of Google login button
So In my react application I have filtered option dialog. When a user select filter option and there is no related data, the dialog prompts No objects found based on your filter, try changing filter criteria and below this text I have a button called back to reset the filters. To do so I have defined a function resetAllFilters inside this function I called setStatusFilters.
But when I call resetAllFilters function I got Uncaught TypeError: setStatusFilters is not a function at resetAllFilters
Here is my code.
import { Search } from "#mui/icons-material";
import { Divider, Grid, List, Stack, Typography } from "#mui/material";
import React, { useEffect, useState, useMemo } from "react";
import TextInput from "../../../components/input/TextInput";
import Loading from "../../../components/loading/Loading";
import { filterObjects, filterSearch } from "../../../data/helpers/Helpers";
import TrackingFilterContainer from "../containers/TrackingFilterContainer";
import ObjectListItem from "./ObjectListItem";
import {
makeDefaultFilterState,
makeFilterOptionsObj,
} from "../../../data/helpers/Helpers";
import Button from "../../../components/button/Button";
const classes = {
Root: {
height: "100vh",
},
SearchInput: (theme) => ({
mt: "5%",
// ml: "5%",
p: 0.7,
borderRadius: "5px",
width: "100%",
bgcolor: "white",
"& input": {
overflow: "hidden",
textOverflow: "ellipsis",
},
"& svg": {
mr: "2.5%",
},
[theme.breakpoints.down("md")]: {
mt: 0,
pl: 1.5,
bgcolor: "background.primary",
},
}),
SearchLogo: { color: "misc.hint" },
Divider: (theme) => ({
mt: "2.5%",
width: "100%",
mb: 3,
[theme.breakpoints.down("md")]: {
display: "none",
},
}),
FilterWrapper: {
// mt: "3%",
px: "2%",
pt: "5%",
pb: "3%",
columnGap: "3%",
},
ListWrapper: {
// mt: "1.5%",
height: "100%",
overflow: "auto",
"&::-webkit-scrollbar": {
width: "3px",
},
"&::-webkit-scrollbar-thumb": {
borderRadius: "8px",
backgroundColor: "misc.hint",
},
},
};
function ObjectList({
search,
setSearch,
trackingObjects,
loading,
error,
selectedObj,
setSelectedObj,
statusFilters,
setStatusCheckboxFilters,
setStatusFilters,
}) {
const handleClick = React.useCallback((obj) => {
setSelectedObj(obj);
}, []);
console.log(statusFilters, "object");
const resultObjs = filterSearch(
filterObjects(trackingObjects, statusFilters),
search
);
const [res, setReset] = useState(false);
const reset = () => setReset(!setStatusFilters);
const objsWithLocations = resultObjs.filter(
({ location }) =>
location?.[0] !== null &&
location?.[0] !== undefined &&
location?.[1] !== null &&
location?.[1] !== undefined
);
const defaultFilterState = useMemo(
() => makeDefaultFilterState(trackingObjects),
[trackingObjects]
);
const filterOptionsObj = useMemo(
() => makeFilterOptionsObj(trackingObjects),
[trackingObjects]
);
const resetAllFilters = () => {
setStatusFilters([]);
};
// console.log('objsWithLocations', objsWithLocations);
return (
<Grid container direction="column" sx={classes.Root} wrap="nowrap">
<Stack
sx={{
flexDirection: { xs: "row", md: "column" },
alignItems: { xs: "center", md: "flex-start" },
justifyContent: { xs: "space-between", md: "flex-start" },
px: 1.0,
mb: 2,
}}
>
<TextInput
autoComplete="off"
variant="standard"
placeholder="Search for an object...."
value={search}
onValueChange={setSearch}
InputProps={{
startAdornment: <Search sx={classes.SearchLogo} />,
disableUnderline: true,
}}
sx={classes.SearchInput}
/>
<Divider sx={classes.Divider} />
<TrackingFilterContainer
defaultFilterState={defaultFilterState}
filterOptionsObj={filterOptionsObj}
/>
</Stack>
{loading && !resultObjs?.length && <Loading />}
{error && (
<Typography
variant="h5"
color="text.secondary"
mx="auto"
mt="5%"
width="45%"
textAlign="center"
>
{error}
</Typography>
)}
{!loading && objsWithLocations?.length === 0 ? (
<Typography
variant="h5"
color="text.secondary"
mx="auto"
mt="5%"
width="45%"
textAlign="center"
// onClick={() => defaultFilterState()}
>
No objects found based on your filter, try changing filter criteria
<Button
variant="text"
text="Back"
sx={{
fontSize: "24px",
p: 0,
verticalAlign: "baseline",
ml: "6px",
textTransform: "none",
}}
onClick={() => resetAllFilters()}
/>
</Typography>
) : (
<List sx={classes.ListWrapper}>
{objsWithLocations.map((obj) => (
<ObjectListItem
key={obj.trackingObjectId}
object={obj}
handleClick={handleClick}
isActive={selectedObj?.trackingObjectId === obj.trackingObjectId}
/>
))}
</List>
)}
</Grid>
);
}
export default ObjectList;
// export default React.memo(ObjectList);
Here is ObjectList.js rendered
import {Grid} from "#mui/material";
import {useNavigate} from "react-router-dom";
import {useTheme} from "#mui/material/styles";
import useMediaQuery from "#mui/material/useMediaQuery";
import React, {useEffect, useState} from "react";
import {TrackingPageItems} from "../../data/constants/TrackingObject";
import Map from "./map/Map";
import MobileNavigate from "./mobileNavigate/MobileNavigate";
import ObjectList from "./objectList/ObjectList";
import ObjectOne from "./objectOne/ObjectOne";
import buildSocketIoConnection from "../../data/socket.io/client";
import API from "../../data/api/API";
import {handleError} from "../../data/helpers/apiHelpers";
import {checkAuthStatus} from "../../data/helpers/Helpers";
import {filterObjects, filterSearch} from "../../data/helpers/Helpers";
function TrackingPage({
trackingObjects,
updateTrackingObjects,
setTrackingObjects,
selectedObject,
setSelectedObject,
...props
}) {
const [objectListSearch, setObjectListSearch] = useState("");
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
const [selectedTab, setSelectedTab] = useState(TrackingPageItems.LIST);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const navigate = useNavigate();
const handleTabChange = (newTab) => {
setSelectedTab(newTab);
};
useEffect(() => {
const userInfoString = localStorage.getItem("userInfo");
const userInfo = JSON.parse(userInfoString);
const token = userInfo?.token;
let possibleSocketIoConnection = null;
if (token) {
possibleSocketIoConnection = buildSocketIoConnection(token, updateTrackingObjects);
} else {
console.error("No token provided. Won't connect to socket.io server");
}
return () => {
setSelectedObject(null);
possibleSocketIoConnection?.closeConnection?.();
};
}, []);
const fetchObjectList = React.useCallback((firstTime = false) => {
firstTime && setLoading(true);
API.object
.getObjectList()
.then((objs) => {
// console.log('TrackingObjects', objs.data);
setTrackingObjects(objs.data);
})
.catch((err) => {
console.log(err);
checkAuthStatus(err.response?.status, navigate);
setError(handleError(err, "getObjectList"));
})
.finally(() => {
firstTime && setLoading(false)
});
}, []);
useEffect(() => {
fetchObjectList(!trackingObjects?.length);
// const interval = setInterval(fetchObjectList, 20000);
// return () => clearInterval(interval);
}, []);
const resultObjs = filterSearch(filterObjects(trackingObjects, props.statusFilters), objectListSearch);
return (
<>
<MobileNavigate selectedTab={selectedTab} handleTabChange={handleTabChange} />
<Grid container direction="row" spacing={1}>
{(!isMobile || (isMobile && selectedTab === TrackingPageItems.LIST)) && (
<Grid item container direction="column" xs={12} md={3}>
{selectedObject ? (
<ObjectOne trackingObjects={trackingObjects} selectedObj={selectedObject} setSelectedObj={setSelectedObject} />
) : (
<ObjectList
search={objectListSearch}
setSearch={setObjectListSearch}
selectedObj={selectedObject}
setSelectedObj={setSelectedObject}
trackingObjects={trackingObjects}
loading={loading}
error={error}
{...props}
/>
)}
</Grid>
)}
{(!isMobile || (isMobile && selectedTab === TrackingPageItems.MAP)) && (
<Grid item container direction="column" xs={12} md={9}>
<Map
loading={loading}
selectedObj={selectedObject}
setSelectedObj={setSelectedObject}
trackingObjects={resultObjs}
{...props}
/>
</Grid>
)}
</Grid>
</>
);
}
export default React.memo(TrackingPage);
How can I solve this error?
I'm running into an issue that I can't quite figure out. I'm building a Wordle clone, the state seems to be updating on some events and not on others, and I can't quite track down why.
I have a Keyboard component, which takes handleKeyClick as a prop from the parent component, and that is attached to two event handlers.
Parent Component
import { Box, Divider, Grid, Typography } from "#mui/material";
import { useState, useEffect, useCallback } from 'react';
import Keyboard from "../Keyboard";
import { v4 as uuid } from 'uuid'
import WordleNotifbar from "../WordleNotifBar";
import Loading from "../Utils/Loading";
import { IGuessState } from "../../types";
interface IGuessGridProps {
addGuess: Function,
guesses: any,
answer: any
}
const GuessGrid = (props: IGuessGridProps) => {
const { addGuess, guesses, answer } = props;
let [notif, setNotif] = useState<boolean>(false);
const [guess, setGuess] = useState<string[]>([]);
const styles = {
input: {
border: ".5px solid white",
height: "50px",
display: "flex",
borderRadius: "5px",
justifyContent: "center",
alignItems: "center",
backgroundColor: "",
color: "white",
},
container: {
minWidth: "300px",
width: "30%",
maxWidth: "450px",
margin: "0 auto",
marginTop: "15px",
},
}
// In the parent component, I have defined the function I'm passing in as a prop as such:
const handleAddCharacter = (char: string) => {
setGuess([...guess, char])
}
// Not fully implemented yet
const handleBackspace = (e: MouseEvent): void => {
e.preventDefault();
setGuess([...guess])
}
const handleSubmit = (): void => {
let word = guess.join('')
if (word.length === answer.length) {
addGuess(word.toLowerCase())
setGuess([]);
}
else {
setNotif(true);
setTimeout(() => {
setNotif(false);
}, 1000)
}
}
if (answer) {
return <>
<Divider />
<Grid container sx={styles.container} >
{answer.split('').map((_: string, index: number) => {
return (<Grid item xs={12 / answer.length} sx={styles.input} key={uuid()}>
<Box>
<Typography>
{guess[index]}
</Typography>
</Box>
</Grid>)
})}
</Grid>
<Keyboard guesses={guesses} answer={answer} handleKeyClick={handleAddCharacter} handleBackspace={handleBackspace} submitFunc={handleSubmit} />
{notif ? <WordleNotifbar message="Not Enough Characters" duration={1000} /> : ""}
</>;
} else {
return <Loading />
}
};
export default GuessGrid;
Keyboard Component
import { Box, Grid, SxProps, Theme, Typography } from "#mui/material";
import { useCallback, useEffect, useState } from 'react';
import { v4 as uuid } from 'uuid';
import BackspaceIcon from '#mui/icons-material/Backspace';
import React from "react";
interface IKeyboardProps {
guesses: string[],
answer: string,
handleKeyClick: any,
submitFunc: any,
handleBackspace: any
}
const Keyboard = (props: IKeyboardProps) => {
const { guesses, answer, handleKeyClick, submitFunc, handleBackspace } = props
const [guessedLetters, setGuessedLetters] = useState<string[]>();
const topRow = 'qwertyuiop'.toUpperCase().split('');
const middleRow = 'asdfghjkl'.toUpperCase().split('');
const bottomRow = 'zxcvbnm'.toUpperCase().split('');
const allKeys = topRow.concat(middleRow.concat(bottomRow));
// When the component is initialized, I am establishing an event listener in the window for the key press events.
useEffect(() => {
window.addEventListener('keypress', handlePhysicalKeyPress)
}, [])
useEffect(() => {
const allGuessedCharacters = guesses.join('').split('');
const uniqueGuessedCharacters = allGuessedCharacters.filter((val: string, index: number, self) => self.indexOf(val) === index)
setGuessedLetters(uniqueGuessedCharacters);
}, [guesses])
const handleVirtualKeyPress = (e: any) => {
handleKeyClick(e.target.textContent)
}
const handlePhysicalKeyPress = (e: KeyboardEvent) => {
e.preventDefault()
if (allKeys.includes(e.key.toUpperCase())) {
handleKeyClick(e.key.toUpperCase());
}
}
const genKeyStyles = (character: string, _: number): SxProps<Theme> => {
character = character.toLowerCase()
const styles = {
width: character === "bs" || character === "enter" ? "63px" : "33px",
marginX: "1px",
marginY: "1px",
borderRadius: "5px",
height: "50px",
color: "black",
textAlign: "center",
backgroundColor: "#DDD",
display: "flex",
justifyContent: "center",
alignItems: "center",
};
if (guessedLetters) {
if (answer.indexOf(character) >= 0 && guessedLetters.indexOf(character) >= 0) {
styles.backgroundColor = "green"
} else if (answer.indexOf(character) < 0 && guessedLetters.indexOf(character) >= 0) {
styles.backgroundColor = "#777"
}
}
return styles
}
return <Box sx={{ display: "flex", flexDirection: "column", justifyContent: "center", marginTop: "10px", }}>
<Box sx={{ display: "flex", justifyContent: "center" }}>
{topRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
</Box>
<Box sx={{ display: "flex", justifyContent: "center" }}>
{middleRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
</Box>
<Box sx={{ display: "flex", justifyContent: "center" }}>
<Box sx={genKeyStyles("enter", 1)} key={uuid()} onClick={submitFunc}>
<Typography key={uuid()}>enter</Typography>
</Box>
{bottomRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
<Box sx={genKeyStyles("bs", 1)} key={uuid()} onClick={handleBackspace}>
<Typography key={uuid()}><BackspaceIcon /></Typography>
</Box>
</Box>
</Box>
};
export default Keyboard;
What happens is that the virtual key press seems to update the state properly, but the physical keypress seems to reset the state back to an empty array. I can't really figure out a good reason why this is happening. Any thoughts? I appreciate your help in advance!
Link to Live Application
When you do:
useEffect(() => {
window.addEventListener('keypress', handlePhysicalKeyPress)
}, [])
...you are attaching a specific handlePhysicalKeyPress function as event listener. But that function is re-created at each component re-render, so you no longer reference the "current" function "version" (should you try to remove it, you would not be able to because it is no longer the same reference).
As such, the actual listener is the very first "version" of your function, which calls the very first "version" of your handleKeyClick prop, which is the very first "version" of your handleAddCharacter function, which knows only the very first version of your guess state... which is an empty array.
That is why when handlePhysicalKeyPress is executed by a key press, it builds a new guess array from an empty array.
While you should avoid this discrepancy between what you attach to your event listener and your actual "render-time" function, there should be a very simple solution to your specific case: should you use the functional form of your state setter, even if it is the "very first version", it should use the "current" state version:
setGuess((currentGuess) => [...currentGuess, char])
I have mui v5 dialog that I am not able to set its width using style() component.
import {
Dialog,
DialogContent,
DialogTitle,
Paper,
Typography,
} from "#mui/material";
import { Close } from "#mui/icons-material";
import { styled } from "#mui/material/styles";
import ActionButton from "./ActionButton";
import React from "react";
const StyledDialog = styled(Dialog)(({ theme }) => ({
fullWidth: true, // it's not taking effect
maxWidth: "lg", // it's not taking effect
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5),
"& MuiDialog-paper": {
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5),
},
"& .MuiTypography-h6": {
paddingRight: "0px",
},
}));
export default function Popup(props) {
const { title, children, openPopup, setOpenPopup } = props;
return (
<StyledDialog open={openPopup} onClose={() => setOpenPopup(false)}>
<DialogTitle>
<div style={{ display: "flex" }}>
<Typography variant="h6" component="div" style={{ flexGrow: 1 }}>
{title}
</Typography>
<ActionButton
color="secondary"
onClick={() => setOpenPopup(false)}
>
<Close />
</ActionButton>
</div>
</DialogTitle>
<DialogContent dividers>{children}</DialogContent>
</StyledDialog>
However, if I listed the props directly inside the it works.
<StyledDialog fullWidth="true" maxWidth="lg">
</StyledDialog>
What is the correct way of setting up the width and maxWidth.
Try this on the styledDialog declaration:
const StyledDialog = styled((props) => (
<Dialog
fullWidth={true}
maxWidth={'lg'}
{...props}
/>
))(({ theme }) => ({
// Your code to style the dialog goes here
}));
The problem on your code is that you arent passing the properties fullWidth and maxWidth to the component.
You are trying to use maxWidth: lg and fullWidth: true like css, but they are only for the Dialog API.
So you could use maxWidth in the component as an API like
<Dialog maxWidth="lg" fullWidth ... >
or you can add it as css style using theme.
const StyledDialog = styled(Dialog)(({ theme }) => ({
maxWidth: theme.breakpoints.values.lg, // there's no styling such fullWidth
...
}));
You could have a look at the default theme.
I am new to Next js, I want to call the news api in useEffect and dispatch the news array to my store. then filter it based on the user's input in the search bar in the header. problem is once the use effect data fetching is done and user starts typing rather than filtering the news array the screen gets re-render and the data fetching starts again. how to prevent this re-rendering and save the news array?
I tried to use getStaticprops but useDispatch is not allowed in there.
index.js
import { fetchNews } from "../store/actions/newsActions";
import NewsInfo from "../components/NewsInfo";
import { useDispatch, useSelector } from "react-redux";
import CircularProgress from "#material-ui/core/CircularProgress";
export default function Home() {
const dispatch = useDispatch();
const { news } = useSelector((state) => state.news);
React.useEffect(() => {
dispatch(fetchNews());
}, []);
return (
<>
{/* this wrapper cmp will make each headline uniquely accessible */}
{news?.articles ? (
news.articles.map((article) => (
<React.Fragment key={article.publishedAt}>
<NewsInfo headlines={article} />
</React.Fragment>
))
) : (
<CircularProgress />
)}
</>
);
}
Header.js
import React from "react";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import Typography from "#material-ui/core/Typography";
import InputBase from "#material-ui/core/InputBase";
import { fade, makeStyles } from "#material-ui/core/styles";
import MenuIcon from "#material-ui/icons/Menu";
import SearchIcon from "#material-ui/icons/Search";
import { useDispatch } from "react-redux";
import { filterHeadlines } from "../store/actions/newsActions";
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
display: "none",
[theme.breakpoints.up("sm")]: {
display: "block",
},
},
search: {
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto",
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
inputRoot: {
color: "inherit",
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
function SearchAppBar() {
const classes = useStyles();
const dispatch = useDispatch();
const [input, setInput] = React.useState("");
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="open drawer"
>
<MenuIcon />
</IconButton>
<Typography className={classes.title} variant="h6" noWrap>
Material-UI
</Typography>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ "aria-label": "search" }}
value={input}
onChange={(e) => setInput(e.target.value)}
onBlur={() => dispatch(filterHeadlines(input))}
/>
</div>
</Toolbar>
</AppBar>
</div>
);
}
export default SearchAppBar;
you can use store dispatch to getServersideProps, getstaticprops
from your store file
export const rootDispatch = store.dispatch;