输入正在失去对挂钩更新的关注

Input is loosing focus on hooks update

我是 reactjs 的新手,我正在尝试从输入中读取数据。问题是当我输入一个符号时,我的输入失去了焦点。但只有当所有逻辑都在函数内部时。 当带按钮的输入和逻辑在不同的文件中时 - 它正在工作。我真的不知道为什么...

我已经用相同的代码创建了单独的文件并将其导入到解决方案中 - 没关系。 我试过 onChange={handleChange} - 也失去了焦点。

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>
)}

我也是 React 的新手,所以请对我的解释持保留态度(希望其他人可以详细说明)。我相信它与嵌套组件以及 React 的方式有关 re-rendering。 .

如果您使用 SearchComponent 作为变量而不是匿名函数,这将按预期工作。

我也很好奇为什么使用这样的嵌套函数(当使用 JSX 时)会导致这种行为......可能是 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>

即使您将嵌套函数更改为 "actual component",每次按键后焦点都会丢失(又名 onChange)..

无效:

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>
  );
}

发生这种情况是因为 useState 挂钩不是 "hooked" 您的 SearchComponent,而是您的 MyInput 组件。无论何时,您调用 updateCitySearch() 都会更改 MyInput 的状态,从而迫使 整个 组件变为 re-render。

SearchComponent,在MyInput中明确定义。当 citySearch-state 更新时,SearchComponent 失去焦点,因为它周围的初始 virual DOM 不再完整,取而代之的是一个全新的 DOM。本质上,每次 MyInput 按状态更新时,您都在创建一个全新的 SearchComponent

考虑以下示例:

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>
  );
}

每次更新状态时,都会触发 console.log(),App 组件 re-render 和 SearchComponent 获取 re-created。每次都会渲染 myInput 的新迭代并创建新的 SearchComponent

但是如果你要在 SearchComponent 中定义 useState,那么只有 SearchComponent 会 re-render 状态改变,从而留下原来的 myInput 组件不变,当前SearchComponent完好无损。

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>
  );
}

SearchComponent 是功能组件,不应在另一个组件内定义。在 MyInput 内定义 SearchComponent 将导致重新创建 SearchComponent(而不是重新呈现),本质上它的 DOM 将被删除,然后在每次点击时重新添加。

解决方案非常简单,从 MyInput 中提取 SearchComponent,然后通过 props 对象传递函数和数据:

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>