Я создаю форму, и есть входы, связанные с состоянием. Проблема в том, что когда я печатаю в поле «Имя заказа» или «Требование к выводу», поля ввода продолжают терять фокус. Я читал, что это может произойти, если форма возвращается внутри функции, но для меня это не так.
Я также уверен, что «LabelledIconInput» не является проблемой, поскольку я использую его на странице входа в систему, и у меня нет такой проблемы.
Может ли кто-нибудь дать какие-либо указатели, почему это может происходить?
Входные данные, имеющие проблему (они определены в отдельных файлах, находящихся в /components/):
<LabelledDropdown
label = "SEASON*"
selectedValue = {seasonName}
placeholder = "Select a season"
options = {seasons}
onSelectOptions = {handleSeasonName}
></LabelledDropdown>
<LabelledTextArea
label = "OUTPUT REQUIREMENT"
value = {requirements}
placeholder = {null}
onChange = {handleRequirements}
></LabelledTextArea>
Новая форма заказа:
interface INewOrder {}
const NewOrder: React.FC<INewOrder> = (props) => {
const [orderName, setOrderName] = useState("");
const [seasonName, setSeasonName] = useState("");
const [categoryName, setCategoryName] = useState("");
const [requirements, setRequirements] = useState("");
const [dateValue, setDateValue] = useState(null);
const [newUserInfo, setNewUserInfo] = useState({
profileImages: [],
});
const updateUploadedFiles = (files) =>
setNewUserInfo({ ...newUserInfo, profileImages: files });
const handleOrderName = (event) => {
console.info(event);
setOrderName(event);
};
const handleSeasonName = (event) => {
console.info(event);
setSeasonName(event);
};
const handleCategoryName = (event) => {
console.info(event);
setCategoryName(event);
};
const handleDateChange = (event) => {
console.info(event);
setDateValue(event);
};
const handleRequirements = (event) => {
setRequirements(event);
};
return (
<Container>
<MyForm onSubmit = {null}>
<PageTitle>New Order</PageTitle>
<ProgressIndication>
<Dots></Dots>
<Dots></Dots>
<Dots></Dots>
</ProgressIndication>
<br />
<FormBackground>
<Subheading>Tell Us a bit more...</Subheading>
<LabelledIconInput
label = "ORDER NAME"
value = {orderName}
placeholder = "Name"
onChange = {handleOrderName}
></LabelledIconInput>
<br /> <br />
<MySpan>
<LabelledDropdown
label = "SEASON*"
selectedValue = {seasonName}
placeholder = "Select a season"
options = {seasons}
onSelectOptions = {handleSeasonName}
></LabelledDropdown>
<LabelledDropdown
label = "SERVICE CATEGORY"
selectedValue = {categoryName}
placeholder = "Select a Category"
options = {category}
onSelectOptions = {handleCategoryName}
></LabelledDropdown>
</MySpan>
<br /> <br />
<FileUploadComponent
label = "UPLOAD FILES"
multiple
></FileUploadComponent>
<LabelledTextArea
label = "OUTPUT REQUIREMENT"
value = {requirements}
placeholder = {null}
onChange = {handleRequirements}
></LabelledTextArea>
<br />
<LabelledDateInput
label = "Expected Delivery Date"
placeholder = "Select a delivery date"
dateValue = {dateValue}
onChange = {handleDateChange}
></LabelledDateInput>
<br />
<LabelledDropdown
label = "3D Software"
width = "100%"
selectedValue = {software[0].value}
placeholder = "Select a Category"
options = {software}
onSelectOptions = {handleSeasonName}
></LabelledDropdown>
</FormBackground>
<br />
<PrimaryButton onClick = {null}>Next</PrimaryButton>
</MyForm>
</Container>
);
};
Стилизованные компоненты (присутствуют в том же файле, что и форма):
const MyForm = styled("form")`
margin: auto;
`;
const Container = styled("div")`
display: grid;
justify-content: center;
grid-template-columns: auto auto;
`;
const MySpan = styled("span")``;
const PageTitle = styled("h2")`
color: ${colors.theme};
text-align: center;
font-weight: 600;
`;
const FormBackground = styled("div")`
background-color: white;
border-radius: 10px;
padding: 20px;
`;
const Subheading = styled("h3")`
color: ${colors.theme};
font-weight: 500;
`;
const Dots = styled("li")<IDots>`
width: 15px;
height: 15px;
text-align: center;
line-height: 2em;
border-radius: 1em;
background: ${(props) => (props.isActive ? colors.theme : "#e1e5f7")};
margin: 0 50px;
display: inline-block;
color: white;
position: relative;
&&::before {
content: "";
position: absolute;
top: 6px;
left: -100px;
width: 7em;
height: 0.2em;
background: ${(props) => (props.isActive ? colors.theme : "#e1e5f7")};
z-index: -1;
}
&&:first-child::before {
display: none;
}
`;
const ProgressIndicator = styled("div")`
text-align: center;
`;
Получение следующего предупреждения в браузере при загрузке формы:
[Error] Warning: Expected server HTML to contain a matching <div> in <div>.
in div (created by Styled(div))
in Styled(div) (at HyperlinkButton.tsx:21)
in HyperlinkButton (at Navbar.tsx:111)
in div (at Navbar.tsx:109)
in div (created by Styled(div))
in Styled(div) (at Navbar.tsx:108)
in div (created by Styled(div))
in Styled(div) (at Navbar.tsx:99)
in div (at Navbar.tsx:98)
in Navbar (at ProtectedPageWrapper.tsx:44)
in div (created by Styled(div))
in Styled(div) (at ProtectedPageWrapper.tsx:43)
in ProtectedPageWrapper (at new-order.tsx:48)
in Index (created by withI18nextTranslation(Index))
in withI18nextTranslation(Index) (at _app.tsx:36)
in MsalProvider (at _app.tsx:35)
in CookiesProvider (at _app.tsx:34)
in ErrorBoundary (at _app.tsx:33)
in MyApp (created by withI18nextSSR(MyApp))
in withI18nextSSR(MyApp) (created by AppWithTranslation)
in NextStaticProvider (created by withI18nextTranslation(NextStaticProvider))
in withI18nextTranslation(NextStaticProvider) (created by AppWithTranslation)
in I18nextProvider (created by AppWithTranslation)
in AppWithTranslation (created by withRouter(AppWithTranslation))
in withRouter(AppWithTranslation) (at withRedux.tsx:12)
in Provider (at withRedux.tsx:11)
in withRedux(withRouter(AppWithTranslation))
in ErrorBoundary (created by ReactDevOverlay)
in ReactDevOverlay (created by Container)
in Container (created by AppContainer)
in AppContainer
in Root
(anonymous function) (next-dev.js:60)
printWarning (react-dom.development.js:88)
error (react-dom.development.js:60)
warnForInsertedHydratedElement (react-dom.development.js:6603)
didNotFindHydratableInstance (react-dom.development.js:7803)
insertNonHydratedInstance (react-dom.development.js:16504)
tryToClaimNextHydratableInstance (react-dom.development.js:16575)
updateHostComponent (react-dom.development.js:17269)
beginWork$1 (react-dom.development.js:23179)
performUnitOfWork (react-dom.development.js:22154)
workLoopSync (react-dom.development.js:22130)
performSyncWorkOnRoot (react-dom.development.js:21756)
scheduleUpdateOnFiber (react-dom.development.js:21188)
updateContainer (react-dom.development.js:24373)
(anonymous function) (react-dom.development.js:24758)
unbatchedUpdates (react-dom.development.js:21903)
legacyRenderSubtreeIntoContainer (react-dom.development.js:24757)
renderReactElement (index.js:742)
doRender (index.js:904)
tryCatch (runtime.js:45)
invoke (runtime.js:274)
asyncGeneratorStep (index.js:189)
_next (index.js:207)
(anonymous function) (index.js:212)
Promise
(anonymous function) (index.js:204)
_callee$ (index.js:588)
tryCatch (runtime.js:45)
invoke (runtime.js:274)
asyncGeneratorStep (index.js:189)
_next (index.js:207)
promiseReactionJob
Полный код:
import styled from "@emotion/styled";
import React, { useState } from "react";
import PrimaryButton from "../buttons/PrimaryButton";
import LabelledDateInput from "../inputs/LabelledDateInput";
import LabelledDropdown from "../inputs/LabelledDropdown";
import LabelledIconInput from "../inputs/LabelledIconInput";
import { colors } from "../../utilities/colors";
import LabelledTextArea from "../inputs/LabelledTextArea";
import FileUploadComponent from "../inputs/FileUploadComponent";
interface INewOrder {}
interface IDots {
isActive?: boolean;
}
const NewOrder: React.FC<INewOrder> = (props) => {
const MyForm = styled("form")`
margin: auto;
`;
const Container = styled("div")`
display: grid;
justify-content: center;
grid-template-columns: auto auto;
`;
const MySpan = styled("span")``;
const PageTitle = styled("h2")`
color: ${colors.theme};
text-align: center;
font-weight: 600;
`;
const FormBackground = styled("div")`
background-color: white;
border-radius: 10px;
padding: 20px;
`;
const Subheading = styled("h3")`
color: ${colors.theme};
font-weight: 500;
`;
const Dots = styled("li")<IDots>`
width: 15px;
height: 15px;
text-align: center;
line-height: 2em;
border-radius: 1em;
background: ${(props) => (props.isActive ? colors.theme : "#e1e5f7")};
margin: 0 50px;
display: inline-block;
color: white;
position: relative;
&&::before {
content: "";
position: absolute;
top: 6px;
left: -100px;
width: 7em;
height: 0.2em;
background: ${(props) => (props.isActive ? colors.theme : "#e1e5f7")};
z-index: -1;
}
&&:first-child::before {
display: none;
}
`;
const ProgressIndicator = styled("div")`
text-align: center;
`;
const [orderName, setOrderName] = useState("");
const [seasonName, setSeasonName] = useState("");
const [categoryName, setCategoryName] = useState("");
const [requirements, setRequirements] = useState("");
const [dateValue, setDateValue] = useState(null);
const [newUserInfo, setNewUserInfo] = useState({
profileImages: [],
});
const updateUploadedFiles = (files) =>
setNewUserInfo({ ...newUserInfo, profileImages: files });
const seasons = [
{
value: "New Season",
label: "New Season",
},
{
value: "Summer 2022",
label: "Summer 2022",
},
{
value: "Winter 2022",
label: "Winter 2022",
},
];
const category = [
{
value: "Style",
label: "Style",
},
{
value: "Trim",
label: "Trim",
},
{
value: "Fabric",
label: "Fabric",
},
{
value: "Block",
label: "Block",
},
];
const software = [
{
value: "3D Max",
label: "3D Max",
},
{
value: "Unity",
label: "Unity",
},
{
value: "Blender",
label: "Blender",
},
];
const handleOrderName = (event) => {
console.info(event);
setOrderName(event);
};
const handleSeasonName = (event) => {
console.info(event);
setSeasonName(event);
};
const handleCategoryName = (event) => {
console.info(event);
setCategoryName(event);
};
const handleDateChange = (event) => {
console.info(event);
setDateValue(event);
};
const handleRequirements = (event) => {
setRequirements(event);
};
const onFormSubmit = (event) => {
console.info("Form submitted...");
};
const onNextButtonClick = () => {
console.info("Next button clicked");
};
return (
<Container>
<MyForm onSubmit = {onFormSubmit}>
<PageTitle>New Order</PageTitle>
<ProgressIndicator>
<Dots isActive = {true}></Dots>
<Dots></Dots>
<Dots></Dots>
</ProgressIndicator>
<br />
<FormBackground>
<Subheading>Tell Us a bit more...</Subheading>
<LabelledIconInput
label = "ORDER NAME"
value = {orderName}
placeholder = "Name"
onChange = {handleOrderName}
></LabelledIconInput>
<br /> <br />
<MySpan>
<LabelledDropdown
label = "SEASON*"
selectedValue = {seasonName}
placeholder = "Select a season"
options = {seasons}
onSelectOptions = {handleSeasonName}
></LabelledDropdown>
<LabelledDropdown
label = "SERVICE CATEGORY"
selectedValue = {categoryName}
placeholder = "Select a Category"
options = {category}
onSelectOptions = {handleCategoryName}
></LabelledDropdown>
</MySpan>
<br /> <br />
<FileUploadComponent
label = "UPLOAD FILES"
multiple
></FileUploadComponent>
<LabelledTextArea
label = "OUTPUT REQUIREMENT"
value = {requirements}
placeholder = {null}
onChange = {handleRequirements}
></LabelledTextArea>
<br />
<LabelledDateInput
label = "Expected Delivery Date"
placeholder = "Select a delivery date"
dateValue = {dateValue}
onChange = {handleDateChange}
></LabelledDateInput>
<br />
<LabelledDropdown
label = "3D Software"
width = "100%"
selectedValue = {software[0].value}
placeholder = "Select a Category"
options = {software}
onSelectOptions = {handleSeasonName}
></LabelledDropdown>
</FormBackground>
<br />
<PrimaryButton onClick = {onNextButtonClick}>Next</PrimaryButton>
</MyForm>
</Container>
);
};
export default NewOrder;
Компоненты @DrewReese LabelledDropdown и LabelledTextArea присоединены вверху. Я также отредактировал вопрос, указав, где они находятся, и добавил в конце «Стилизованные компоненты». Надеюсь теперь понятно?
Добавлен полный код @DrewReese
что-то вызывает повторный рендеринг, и поэтому он теряет фокус.
У меня была аналогичная проблема внутри вкладок начальной загрузки. Хотя я не мог понять. Но способ, который я сделал, это переменная состояния, которая повторно отрисовывала, я поместил ее в useEffect и вернул фокус на поле ввода. Однако это не очень хороший подход, но, по крайней мере, в этом причина.
Итак, приведенный ниже код - это то, что я сделал. Таким образом, при каждом изменении successOrderReport я бы возвращал поле ввода обратно.
const searchInput = useRef();
useEffect(() => {
searchInput.current.focus();
}, [successfulorderReport]);
вы можете попробовать это для тестирования, но для вас я бы посоветовал сначала посмотреть, что вызывает повторный рендеринг.
Спасибо @Wahab, на самом деле я потратил некоторое время, пытаясь понять, почему запускается повторный рендеринг. Как видите, в операторе return возвращается простая форма. К сожалению, я не могу использовать здесь useRef, потому что мне не разрешено изменять функциональность LabelledIconInput.
Вы смогли выяснить, почему он перерисовывается?
Нет, я еще не мог. Вот почему я спрашиваю здесь.
Проблема в том, что вы объявили все стилизованные компоненты внутри другого компонента React. Каждый раз, когда NewOrder
перерисовывает, он повторно объявляет компонент MyForm
, который перемонтирует его и все его компоненты поля формы. Любое поле, которое имело фокус, будет перемонтировано и потеряет фокус.
Определяйте стилизованные компоненты вне метода рендеринга
It is important to define your styled components outside of the render method, otherwise it will be recreated on every single render pass. Defining a styled component within the render method will thwart caching and drastically slow down rendering speed, and should be avoided.
Тело функции весь функционального компонента React является "метод рендеринга".
Объявляйте все стилизованные компоненты отдельно, вне других компонентов React.
const MyForm = styled("form")`
margin: auto;
`;
const Container = styled("div")`
...
`;
const MySpan = styled("span")``;
const PageTitle = styled("h2")`
...
`;
const FormBackground = styled("div")`
...
`;
const Subheading = styled("h3")`
...
`;
const Dots = styled("li")<IDots>`
...
`;
const ProgressIndicator = styled("div")`
text-align: center;
`;
const NewOrder: React.FC<INewOrder> = (props) => {
...
Где объявлены/определены компоненты
LabelledDropdown
иLabelledTextArea
? Что такое «Стильные компоненты», которые были скрыты для краткости? stackoverflow.com/help/минимально-воспроизводимый-пример