#javascript #reactjs #redux #material-ui
#javascript #reactjs #redux #материал-пользовательский интерфейс
Вопрос:
Я пытаюсь добавить панель закусок, чтобы отображать сообщение всякий раз, когда пользователь входит в систему или нет. SnackBar.jsx:
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error";
import CloseIcon from "@material-ui/icons/Close";
import green from "@material-ui/core/colors/green";
import IconButton from "@material-ui/core/IconButton";
import Snackbar from "@material-ui/core/Snackbar";
import SnackbarContent from "@material-ui/core/SnackbarContent";
import { withStyles } from "@material-ui/core/styles";
const variantIcon = {
success: CheckCircleIcon,
error: ErrorIcon
};
const styles1 = theme => ({
success: {
backgroundColor: green[600]
},
error: {
backgroundColor: theme.palette.error.dark
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing.unit
},
message: {
display: "flex",
alignItems: "center"
}
});
function SnackbarContentWrapper(props) {
const { classes, className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={classNames(classes[variant], className)}
aria-describedby="client-snackbar"
message={(
<span className={classes.message}>
<Icon className={classNames(classes.icon, classes.iconVariant)} />
{message}
</span>
)}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>
]}
{...other}
/>
);
}
SnackbarContentWrapper.propTypes = {
classes: PropTypes.shape({
success: PropTypes.string,
error: PropTypes.string,
icon: PropTypes.string,
iconVariant: PropTypes.string,
message: PropTypes.string,
}).isRequired,
className: PropTypes.string.isRequired,
message: PropTypes.node.isRequired,
onClose: PropTypes.func.isRequired,
variant: PropTypes.oneOf(["success", "error"]).isRequired
};
const MySnackbarContentWrapper = withStyles(styles1)(SnackbarContentWrapper);
const CustomizedSnackbar = ({
open,
handleClose,
variant,
message
}) => {
return (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
autoHideDuration={6000}
onClose={handleClose}
>
<MySnackbarContentWrapper
onClose={handleClose}
variant={variant}
message={message}
/>
</Snackbar>
</div>
);
};
CustomizedSnackbar.propTypes = {
open: PropTypes.bool.isRequired,
handleClose: PropTypes.func.isRequired,
variant: PropTypes.string.isRequired,
message: PropTypes.string.isRequired
};
export default CustomizedSnackbar;
SignInFormContainer.jsx:
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import SnackBar from '../../components/SnackBar';
import SignInForm from './SignInForm';
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(false);
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
if (variant) {
setSnackBarState(true);
}
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
SingInContainer.propTypes = {
variant: PropTypes.string.isRequired,
message: PropTypes.string.isRequired
}
const mapStateToProps = (state) => {
const {variant, message } = state.snackBar;
return {
variant,
message
}
}
export default connect(mapStateToProps)(SingInContainer);
Когда я запускаю приложение, я получаю эту ошибку:
Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at invariant (http://localhost:9000/bundle.js:34484:15)
at dispatchAction (http://localhost:9000/bundle.js:47879:44)
at SingInContainer (http://localhost:9000/bundle.js:79135:5)
at renderWithHooks (http://localhost:9000/bundle.js:47343:18)
at updateFunctionComponent (http://localhost:9000/bundle.js:49010:20)
at beginWork (http://localhost:9000/bundle.js:50020:16)
at performUnitOfWork (http://localhost:9000/bundle.js:53695:12)
at workLoop (http://localhost:9000/bundle.js:53735:24)
at HTMLUnknownElement.callCallback (http://localhost:9000/bundle.js:34578:14)
at Object.invokeGuardedCallbackDev (http://localhost:9000/bundle.js:34628:16)
Проблема связана с компонентом SnackBar. Я использую useState
перехваты, чтобы изменить состояние панели закусок. Должен ли я использовать класс и a componentShouldUpdate
, чтобы не выполнять рендеринг несколько раз?
Комментарии:
1. Вероятно, вы используете Webpack, и в этом случае я хотел бы обратить ваше внимание на опцию devtool , которая позволяет вам точно определить местонахождение ошибки в вашем исходном коде, вместо того, чтобы определять местоположение ошибки в вашем связанном коде, как у вас есть сейчас
2. Можете ли вы проверить, вызывается ли
handleClose
несколько раз и решит ли проблему изменениеhandleClose={handleClose}
наhandleClose={()=>handleClose}
?3. @Nicholas Я попробовал это, но я получил ту же ошибку
4. у меня также была похожая проблема, но в моем случае это было из-за устаревших значений из предыдущих отображений, я передал пустой массив зависимостей в useCallback и без необходимости обновлял состояние.
5. У меня была похожая ошибка, но это было из-за api, вызов которого был неудачным, и в api были автоматические отключения.
Ответ №1:
Я подозреваю, что проблема заключается в том, что вы вызываете свой установщик состояния непосредственно внутри тела компонента функции, что заставляет React повторно вызывать вашу функцию снова, с теми же реквизитами, что в конечном итоге приводит к повторному вызову установщика состояния, который запускает React для повторного вызова вашей функции …. и так далее.
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(false);
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
if (variant) {
setSnackBarState(true); // HERE BE DRAGONS
}
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
Вместо этого я рекомендую вам просто условно установить значение по умолчанию для свойства state, используя троичное значение, так что в итоге вы получите:
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(variant ? true : false);
// or useState(!!variant);
// or useState(Boolean(variant));
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
Полная демонстрация
Смотрите это CodeSandbox.io demo для полной демонстрации его работы, плюс сломанный компонент, который у вас был, и вы можете переключаться между ними.
Комментарии:
1. ваш ответ решает проблему, описанную в вопросе, однако переменная
open
всегдаfalse
2. @para008 Я добавил всеобъемлющую демонстрацию , чтобы доказать, что это работает так, как должно.
3. Я не нахожу свою ошибку, потому что
open
однако для меня всегда false !!variant вначале равен false, затем становится true4. возможно, мне следует задать это в другом вопросе. Спасибо за ваш ответ.
5. Ваш ответ решает мою проблему, спасибо!
Ответ №2:
В SnackbarContentWrapper
вам нужно изменить
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose} // change this
>
Для
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => onClose()} // to this
>
Так что действие запускается только при нажатии.
В качестве альтернативы, вы могли бы просто использовать handleClose
in SignInContainer
для
const handleClose = () => (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
Это то же самое.
Комментарии:
1. Спасибо, у меня работает. Это то, что я искал onClick={() => OnClose} .
2. может кто-нибудь подсказать мне, что погуглить, чтобы понять разницу между onClick ={() => OnClose} и onClick = {OnClose}? Спасибо!
3. Поиск в Google чего-либо вроде «привязки встроенных функций в react» должен привести вас туда, куда вам нужно. вам придется узнать об «этом» в js и функциях каррирования, это настоящая кроличья нора.
4. Именно то, что сработало для меня. Простой, но эффективный ответ. Премного благодарен.
5. разница между onClick={() => OnClose} и onClick={OnClose}?
Ответ №3:
Вы должны связать событие в вашем onClick. Кроме того, функция click должна получать событие. Смотрите пример
export default function Component(props) {
function clickEvent (event, variable){
console.log(variable);
}
return (
<div>
<IconButton
key="close"
aria-label="Close"
color="inherit"
onClick={e => clickEvent(e, 10)}
>
</div>
)
}
Ответ №4:
При вызове функции добавьте событие, подобное этому
перед
<button onClick={ decrementCount() }>-</button>
после
<button onClick={ () => decrementCount() }>-</button>
Комментарии:
1. Это помогло мне, но зачем это нужно делать? Просто хочу прояснить свои концепции, поскольку я новичок в JS.
Ответ №5:
Вам нужно добавить событие, прежде чем вызывать вашу функцию handleFunction следующим образом:
function SingInContainer() {
..
..
handleClose = () => {
}
return (
<SnackBar
open={open}
handleClose={() => handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
)
}
Ответ №6:
Из того, что я собрал, похоже, что ошибка возникает, когда мы вызываем так много функций.
в основном
setState()
Одним из способов обойти это будет установка состояний внутри функции вместо setState
прямого вызова onClick
, например.
Использование большого количества состояний, подобных этому, вероятно, приводит к повторному рендерингу react несколько раз, вызывая эту ошибку.
Ответ №7:
Просто помните, что если вы передаете функцию в круглых скобках, функция будет выполняться каждый раз при рендеринге компонента. Чтобы предотвратить это, попробуйте поставить ()=>
перед вашей функцией.
Ответ №8:
У меня тоже такая проблема, и решение в том, что я не привязал событие в моем onClick. поэтому, когда он рендерится в первый раз и данных становится больше, что в конечном итоге приводит к повторному вызову установщика состояния, который запускает React для повторного вызова вашей функции и так далее.
export default function Component(props) {
function clickEvent (event, variable){
console.log(variable);
}
return (
<div>
<IconButton
key="close"
aria-label="Close"
color="inherit"
onClick={e => clickEvent(e, 10)} // or you can call like this:onClick={() => clickEvent(10)}
>
</div>
)
}
Ответ №9:
Вы можете предотвратить эту ошибку, используя перехваты внутри функции
Ответ №10:
ЭТА ОШИБКА ВЫЗВАНА ТЕМ, ЧТО ВЫ ИСПОЛЬЗУЕТЕ ПАРАНТЕЗИС ПОСЛЕ ИМЕНИ ФУНКЦИИ В ОТВЕТ.
import React, { useState } from "react";
const Count = () => {
const [num, setnum] = useState(0)
const increment = () => {
setnum((prev) => {
return prev 1
})
setnum((prev) => {
return prev 1
})
}
const decrement = () => {
setnum(num - 1)
}
return (
<div>
<button onClick={increment}> </button>
{num}
<button onClick={decrement}>-</button>
</div>
)
}
экспортируйте количество по умолчанию;
НЕ ИСПОЛЬЗУЙТЕ ::: onClick= {increment()} или onClick = {decrement()} в разделе Return
Ответ №11:
Итак, по сути, вы просто хотите убедиться, что вы вызываете свой установщик состояния в качестве обратного вызова для прослушивателя событий, чтобы таким образом функция не запускалась до тех пор, пока не будет выполнено действие.
Что-то вроде этого:
onClick={() => stateSetterFunction()}
Ответ №12:
В моем случае его причина из-за неправильного attribute
имени onblur={setFieldTouched('firstName')} -->onBlur={()=>setFieldTouched('firstName')}
. после исправления ошибки имени атрибута исчезла
Ответ №13:
Я думаю, вы можете это сделать. У меня дома это работает без каких-либо проблем.
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false);
};
<SnackBar
open={open}
handleClose={()=>handleClose(r)}
variant={variant}
message={message}
/>
Ответ №14:
Убедитесь, что вы включили preventDefault() или нет. В моем случае я забыл использовать это, и позже я нахожу эту ошибку.
Ответ №15:
У меня была похожая проблема, однако мой сценарий был другим. useEffect(()=>{},[])
спасло меня от этой ошибки.
Ответ №16:
Вероятно, вы сразу вызываете свой setState, когда передаете его, убедитесь, что вы не устанавливаете его таким образом:
переменная={setState()}
вместо этого сделайте это следующим образом:
переменная={setState()}
иногда у вас есть setState в функции, и вы вызываете функцию напрямую, когда передаете ее, что вызывает эту проблему.