<div className={`${category} ${accessories}`}>
<div className={clsx(category, accessories)}>
import React, { useCallback } from 'react';
import { Formik, Form, useField } from 'formik';
import * as yup from 'yup';
const initialValues = {
firstName: '',
lastName: '',
nickName: '',
email: '',
password: '',
confirmPassword: ''
};
const validationSchema = yup.object().shape({
firstName: yup.string().required('Имя является обязательным'),
lastName: yup.string().required('Фамилия является обязательной'),
nickName: yup.string().required('Имя пользователя является обязательным'),
email: yup
.string()
.email('Введенное значение не является почтой')
.required('Почта является обязательной'),
password: yup
.string()
.min(8, 'Минимальная длина пароля 8 символов')
.required('Пароль является обязательным'),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), null], 'Пароли не совпадают')
.min(8, 'Минимальная длина пароля 8 символов')
.required('Пароль является обязательным')
});
const ExampleField = ({ name, label, ...props }) => {
const [field, meta, helpers] = useField(name);
return (
<label>
{label}
<input
name={name}
value={field.value}
onChange={field.onChange}
onBlur={field.onBlur}
{...props}
/>
{meta.error !== undefined && meta.touched && <div>{meta.error}</div>}
</label>
);
};
const ExampleForm = () => {
const handleSubmit = useCallback((values, helpers) => {
console.log(values);
helpers.setSubmitting(false);
}, []);
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
validationOnBlur
onSubmit={handleSubmit}
>
{({ values, errors, touched, isValid, isSubmitting }) => (
<Form>
<ExampleField label="Имя" name="firstName" />
<ExampleField label="Фамилия" name="lastName" />
<ExampleField label="Имя пользователя" name="nickName" />
<ExampleField label="Почта" type="email" name="email" />
<ExampleField label="Пароль" type="password" name="password" />
<ExampleField label="Подтверждение пароля" type="password" name="confirmPassword" />
<button type="submit" disabled={!isValid || isSubmitting}>
Отправить
</button>
<pre>{JSON.stringify({ values, errors, touched }, null, 2)}</pre>
</Form>
)}
</Formik>
);
};
const ClickableArea = ({ component: Component = 'div', to, children, ...props }) => {
const history = useHistory();
const control = useRef(null);
const handleClick = useCallback((event) => {
if (event.target === control.current) {
history.push(to);
}
}, [history, control, to]);
return (
<Component ref={control} onClick={handleClick} tabIndex={-1} {...props}>
{children}
</Component>
);
};
<ClickableArea
className={classes.tweet}
component={Paper}
variant="outlined"
to={`/home/tweet/${_id}`}
>...</ClickableArea>
::after
на ссылке растянуть её на весь контейнер. При помощи display: grid/flex
на родителе можно будет поправить всем остальным элементам z-index
. React.StrictMode
отсутствует.App
подписываетесь на изменения store
и обновляете состояние им.src/style.css
, а надо бы для каждого компонента их разделять. Я бы разбивал на модули..jsx
используйте только для компонентов. У Вас есть файлы с логикой, которые имеют такое расширение.sirch-panel.jsx
(а еще лучше бы SearchPanel.jsx
) лучше хранить состоянием только значение поля для новой сущности.store.dispatch
. Диспатчинг событий можно перенести в тот же connect (можно даже bindActionCreators использовать).switch
. Для удаления можете воспользоваться методом массива .filter
. Для пометки important можете воспользоваться методом массива .map
. И вообще его вынести в отдельную директорию, т. к. он не является компонентом (как и action creators).className={important ? "important list-item" : "list-item"}
можете воспользоваться classnames или clsx.id: idCounter
можно будет заменить на Date.now()
. export const ModalBlock: React.FC<ModalBlockProps> = ({
title,
onClose,
isOpen = false,
children,
}: ModalBlockProps): React.ReactElement | null => {
const [closing, setClosing] = useState(false);
const handleClose = () => setClosing(true);
useEffect(() => {
if (isOpen) {
setClosing(false);
}
}, [isOpen]);
if (!isOpen) {
return null;
}
return (
<Dialog TransitionComponent={Transition} open={!closing && isOpen} onExited={onClose} aria-labelledby='form-dialog-title'>
<DialogTitle id='form-dialog-title'>
<IconButton onClick={handleClose} color='secondary' aria-label='close'>
<CloseIcon style={{ fontSize: 26 }} color='secondary' />
</IconButton>
{title}
</DialogTitle>
<DialogContent>{children}</DialogContent>
</Dialog>
);
};
const ThemedInput = withStyles((theme) => ({
root: {
"&:hover": {
"&$underline": {
"&::before": {
borderColor: theme.palette.primary.dark
}
}
}
},
input: {
fontWeight: 700,
color: theme.palette.primary.main
},
underline: {
"&::before": {
borderColor: theme.palette.primary.main
}
}
}))(Input);
const ThemedSelect = withStyles((theme) => ({
select: {
"&:hover": {
"& ~ $icon": {
color: theme.palette.primary.dark
}
}
},
icon: {}
}))(Select);
<ThemedSelect input={<ThemedInput />}></ThemedSelect>
useEffect(() => {
let intervalId = setInterval(async () => {
const response = await fetch('http://127.0.0.1:8000/api/timecalculate', {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
credentials: 'include',
});
const content = await response.json();
const now = new Date();
console.log(content + now);
}, 10000)
return () => {
clearInterval(intervalId);
};
}, []);
useEffect
;deps
передаете пустой массив;const PageSize = () => {
const [pageSize, setPageSize] = useState([document.documentElement.scrollWidth, document.documentElement.scrollHeight]);
const updatePageSize = () => setPageSize([document.documentElement.scrollWidth, document.documentElement.scrollHeight]);
useEffect(() => {
window.addEventListener('resize', updatePageSize);
return () => {
window.removeEventListener('resize', updatePageSize);
};
}, []);
return (
<div>
<p>page width is {pageSize[0]}</p>
<p>page height is {pageSize[1]}</p>
</div>
);
};