Why does a React input element lose focus when nested within a component?

810 views Asked by At

So, I have an input element connected to the React Context API - updating the value onChange works when the element is not nested within a component. Just under the input, I render a different component that returns an input field. This input field is also connected to the context API, but the input loses focus onChange.

I understand that I could add a "key" and even an "id", but none of these solutions seems to work.

Why is this happening, and what is the best way to fix is?

import React, { useContext } from "react";
import { Context } from "../../context";
import { set_employee_action } from "../../context/actions";

const DashBody = () => {
  const { state, dispatch } = useContext(Context);

  const DashboardBody = () => {
    return (
      <div key={"table"}>
        {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT BREAKS */}
        <div key={"LABEL_TWO"}>
          <label htmlFor={"LABEL_TWO"}>{"LABEL_TWO"}:</label>
          <input
            type="text"
            id={"LABEL_TWO"}
            key={"LABEL_TWO"}
            name={"LABEL_TWO"}
            value={
              state.dash.employee_form["LABEL_TWO"]
                ? state.dash.employee_form["LABEL_TWO"]
                : ""
            }
            onChange={(e) => dispatch(set_employee_action(e))}
          ></input>
        </div>
        {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT BREAKS */}
      </div>
    );
  };

  return (
    <div className="dash_body_container" key={"dash_body_container"}>
      {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT WORKS */}
      <div key={"LABEL_ONE"}>
        <label htmlFor={"LABEL_ONE"}>{"LABEL_ONE"}:</label>
        <input
          type="text"
          id={"LABEL_ONE"}
          key={"LABEL_ONE"}
          name={"LABEL_ONE"}
          value={
            state.dash.employee_form["LABEL_ONE"]
              ? state.dash.employee_form["LABEL_ONE"]
              : ""
          }
          onChange={(e) => dispatch(set_employee_action(e))}
        ></input>
      </div>
      {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT WORKS */}
      <DashboardBody></DashboardBody>
    </div>
  );
};

export default DashBody;
1

There are 1 answers

0
Jakub Kosiński On

It looks like you're redefining the DashboardBody component each time DashBody is rendered. You're losing the focus inside DashboardBody's input because you're using dispatch from DashBody so that each time the dispatch is called, DashBody component is re-rendered and is rendering a different DashboardBody component. You can extract DashboardBody and define it outside DashBody but make sure you use useContext(Context) in DashboardBody:

const DashboardBody = () => {
  const { state, dispatch } = useContext(Context); // make sure you have your own dispatch method

    return (
      <div key={"table"}>
        {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT BREAKS */}
        <div key={"LABEL_TWO"}>
          <label htmlFor={"LABEL_TWO"}>{"LABEL_TWO"}:</label>
          <input
            type="text"
            id={"LABEL_TWO"}
            key={"LABEL_TWO"}
            name={"LABEL_TWO"}
            value={
              state.dash.employee_form["LABEL_TWO"]
                ? state.dash.employee_form["LABEL_TWO"]
                : ""
            }
            onChange={(e) => dispatch(set_employee_action(e))}
          ></input>
        </div>
        {/* THIS IS NOT THE ACTUAL PLACE FOR THIS INPUT - BUT THIS IS WHERE IT BREAKS */}
      </div>
    );
  };