Home > Mobile >  React: Text input keep loosing focus as I type
React: Text input keep loosing focus as I type

Time:04-06

I'm building a form and there are inputs binded with state. The problem is that as I type in the field "Order Name" or "Output Requirement" the input fields keep on loosing focus. I have read that this may happen if the form is returned inside a function but that's not the case for me.

I'm also sure that "LabelledIconInput" is not the problem as I'm using it inside the login page and I don't have such a problem there.

Can anyone provide any pointers why this might be happening?

Inputs having the problem (these are defined in a separate files residing in /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>

NewOrder form:

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.log(event);
    setOrderName(event);
  };

  const handleSeasonName = (event) => {
    console.log(event);
    setSeasonName(event);
  };

  const handleCategoryName = (event) => {
    console.log(event);
    setCategoryName(event);
  };

  const handleDateChange = (event) => {
    console.log(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>
  );
};

Styled components (Present inside the same file as the form):

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;
  `;

Getting following warning in the browser when form loads:

[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

Complete code:

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.log(event);
    setOrderName(event);
  };

  const handleSeasonName = (event) => {
    console.log(event);
    setSeasonName(event);
  };

  const handleCategoryName = (event) => {
    console.log(event);
    setCategoryName(event);
  };

  const handleDateChange = (event) => {
    console.log(event);
    setDateValue(event);
  };

  const handleRequirements = (event) => {
    setRequirements(event);
  };

  const onFormSubmit = (event) => {
    console.log("Form submitted...");
  };
  const onNextButtonClick = () => {
    console.log("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;

CodePudding user response:

Issue

The issue is that you've declared all the styled components inside another React component. Each time the NewOrder rerenders it redeclares the MyForm component, which remounts it and all its form field components. Any field that had focus will be remounted and lose focus.

Solution

Define styled components outside of the render method

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.

The entire function body of a React function component is the "render method".

Declare all styled components on their own, outside of other React components.

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) => {
  ...

CodePudding user response:

something is triggering a re render and that's why its losing focus.

I had a similar issue inside bootstrap tabs. Although I couldn't figure it out. But the way around I did was the state variable which had re render i put it inside a useEffect and set the focus back to the input field. This is not a good approach however but atleast that is the reason.

So the below code is what I did. So on every change of successfulOrderReport I would focus the input field back.

  const searchInput = useRef();

  useEffect(() => {
    searchInput.current.focus();
  }, [successfulorderReport]);

you can try this for testing but for you I would suggest is to see what is causing the re render maybe first.

  • Related