Ich bin neu in der Reaktion und versuche, Daten von Eingaben zu lesen. Problem ist, wenn ich ein Zeichen tippe, verliert meine Eingabe den Fokus. Aber nur wenn die gesamte Logik in der Funktion ist. Wenn sich die Eingabe mit Schaltfläche und Logik in einer anderen Datei befindet, funktioniert sie. Ich weiß nicht wirklich warum ...

Ich habe eine separate Datei mit demselben Code erstellt und in sollution importiert - es ist in Ordnung. Ich habe es mit onChange = {handleChange} versucht - auch der Fokus verloren.

export default function MyInput(){
const [citySearch, updateCitySearch] = useState();

function searchCityClick(){
   alert(citySearch);
}
const SearchComponent = ()=> (
    <div> 
        <input 
        value={citySearch}
        onChange={(e) => updateCitySearch(e.target.value)}/>
        <Button variant="contained" color="primary" onClick={searchCityClick}>
            Search
        </Button>
    </div>

);
return(
    <div>
        <div>
            <SearchComponent />
        </div>
    </div>
)}
3
michal 25 Juni 2019 im 20:02

3 Antworten

Beste Antwort

Das SearchComponent ist eine Funktionskomponente und sollte nicht in einer anderen Komponente definiert werden. Wenn Sie SearchComponent in MyInput definieren, wird SearchComponent neu erstellt (nicht neu gerendert), und im Wesentlichen wird das DOM entfernt und bei jedem Klick wieder hinzugefügt.

Die Lösung ist ziemlich einfach: Extrahieren Sie SearchComponent aus MyInput und übergeben Sie die Funktionen und Daten über props Objekt:

const { useState, useCallback } = React;

const SearchComponent = ({ citySearch, updateCitySearch, searchCityClick }) => (
  <div> 
    <input
      value={citySearch}
      onChange={e => updateCitySearch(e.target.value)} />
    
    <button onClick={searchCityClick}>Search</button>
  </div>
);

const MyInput = () => {
  const [citySearch, updateCitySearch] = useState('');

  const searchCityClick = () => alert(citySearch);

  return(
    <div>
      <SearchComponent 
        citySearch={citySearch} 
        updateCitySearch={updateCitySearch}
        searchCityClick={searchCityClick} />
    </div>
  );
};

ReactDOM.render(
  <MyInput />,
  root
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
4
Ori Drori 25 Juni 2019 im 19:21

Dies liegt daran, dass der useState -Hook nicht an Ihre SearchComponent, sondern an Ihre MyInput -Komponente "gebunden" ist. Wenn Sie updateCitySearch() aufrufen, ändern Sie den Status von MyInput und zwingen so die gesamte Komponente zum erneuten Rendern.

SearchComponent wird explizit in MyInput definiert. Wenn citySearch-state aktualisiert wird, verliert SearchComponent den Fokus, da die anfängliche virual DOM Umgebung nicht mehr intakt ist, sondern Sie ein völlig neues DOM-Stück haben. Im Wesentlichen erstellen Sie jedes Mal ein brandneues SearchComponent, wenn das MyInput nach Status aktualisiert wird.

Betrachten Sie das folgende Beispiel:

function App() {
  const [citySearch, updateCitySearch] = useState("");
  console.log("rendered App again"); //always prints
  const SearchComponent = () => {
    console.log("rendered Search"); //always prints
    const searchCityClick = () => {
      alert(citySearch);
    };
    return (
      <div>
        <input
          value={citySearch}
          onChange={e => {
            updateCitySearch(e.target.value);
          }}
        />
        <button onClick={searchCityClick}>Search</button>
      </div>
    );
  };
  return (
    <div>
      <div>
        <SearchComponent />
      </div>
    </div>
  );
}

Jedes Mal, wenn Sie den Status aktualisieren, lösen Sie beide console.log() aus, die App-Komponente wird neu gerendert und SearchComponent wird neu erstellt. Jedes Mal wird eine neue Iteration von myInput gerendert und eine neue SearchComponent erstellt.

Wenn Sie jedoch useState in SearchComponent definieren, werden nur SearchComponent bei Statusänderungen erneut gerendert, sodass die ursprüngliche myInput -Komponente und die aktuelle {{ X4}} intakt.

function App() {
  console.log("rendered App again"); //would never print a 2nd time.
  const SearchComponent = () => {
    const [citySearch, updateCitySearch] = useState("");
    console.log("rendered Search"); //would print with new state change
    const searchCityClick = () => {
      alert(citySearch);
    };
    return (
      <div>
        <input
          value={citySearch}
          onChange={e => {
            updateCitySearch(e.target.value);
          }}
        />
        <button onClick={searchCityClick}>Search</button>
      </div>
    );
  };
  return (
    <div>
      <div>
        <SearchComponent />
      </div>
    </div>
  );
}
1
Christopher Ngo 25 Juni 2019 im 18:17

Ich bin auch neu in React, also nimm meine Erklärung mit einer Prise Salz (hoffentlich kann jemand anderes das näher erläutern). Ich glaube, es hat etwas mit dem Verschachteln von Komponenten und dem erneuten Rendern von React zu tun.

Wenn Sie anstelle einer anonymen Funktion SearchComponent als Variable verwenden, funktioniert dies wie erwartet.

Ich bin auch neugierig, warum die Verwendung solcher verschachtelter Funktionen (bei Verwendung von JSX) dieses Verhalten verursacht ... möglicherweise ein Anti-Pattern?

function MyInput() {
  const [citySearch, updateCitySearch] = React.useState();

  function searchCityClick() {
    alert(citySearch);
  }
  
  const SearchComponent = (
      <div> 
          <input 
          value={citySearch}
          onChange={(e) => updateCitySearch(e.target.value)}/>
          <button variant="contained" color="primary" onClick={searchCityClick}>
              Search
          </button>
      </div>
  );
  
  return (
    <div>
      <div>
        {SearchComponent}
      </div>
    </div>
  );
}


let div = document.createElement("div");
div.setAttribute("id", "app");
document.body.append(div);
ReactDOM.render(<MyInput />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Selbst wenn Sie die verschachtelte Funktion in eine "tatsächliche Komponente" ändern, geht der Fokus nach jedem Tastendruck verloren (auch bekannt als onChange).

Funktioniert nicht:

function MyInput() {
  const [citySearch, updateCitySearch] = React.useState();

  function searchCityClick() {
    alert(citySearch);
  }

  const SearchComponent = () => {
      return (
        <div> 
            <input 
            value={citySearch}
            onChange={(e) => updateCitySearch(e.target.value)}/>
            <button variant="contained" color="primary" onClick={searchCityClick}>
                Search
            </button>
        </div>
    );
  }

  return (
    <div>
      <div>
        <SearchComponent />
      </div>
    </div>
  );
}
0
Matt Oestreich 25 Juni 2019 im 17:46