简体   繁体   English

输入输入时重新渲染超过需要的 React 组件

[英]More than needed React components re-rendering when typing in input

I am taking input from a search input field using searchInput and setSearchInput useState hook and after I press submit button, I call fetchSearchData function providing it the input text and setCompanies hook, where companies are updated with the fetched list of companies from the API.我正在使用searchInputsetSearchInput useState 钩子从搜索输入字段中获取输入,在按下提交按钮后,我调用fetchSearchData function 为其提供输入文本和setCompanies钩子,其中companies使用从 API 获取的公司列表进行更新。

Then companies are passed to another component CompanyList where a map function is called there.然后将companies传递给另一个组件CompanyList ,在那里调用 map function。

The problem is whenever I type in the search field, the CompanyList component is re-rendered although I did not press submit.问题是每当我在搜索字段中键入时, CompanyList组件都会重新呈现,尽管我没有按提交。 I understand that setSearchInput will re-render SearchBar component whenever I type in it, but I don't get why CompanyList re-renders.我知道setSearchInput将在我输入时重新呈现SearchBar组件,但我不明白为什么CompanyList会重新呈现。

Search page source code: Search页面源代码:


const Search = () => {
    const [companies, setCompanies]=useState([]); //List of companies returned from searching
    const [searchInput, setSearchInput] = useState(""); //Search field input

    //Update search text whenever the user types in
    const onSearchChange = (e) => {
        setSearchInput(e.target.value)
    }

    //use the API providing it the search input, and 
    //setCompanies hook to update list of companies
    const onSearchSubmit = (e) => {
        e.preventDefault()
        fetchSearchData(searchInput, setCompanies)
    }


    return (
        <div>
            <Container>
                <Row className={"searchFilterBar"}>
                    <Col sm={6} md={8} className={"searchBar"}>
                        <SearchBar onSubmit={onSearchSubmit} onChange={onSearchChange} value={searchInput} />
                    </Col>
                    <Col sm={6} md={4} className={"filterBar"}>
                    </Col>
                </Row>
                <CompanyList companies={companies} ></CompanyList>
                <Row>
                </Row>
            </Container>
        </div>
    )
}
export default Search;

SearchBar component source code: SearchBar组件源码:

const SearchBar = ({value,onSubmit, onChange}) => {
    return (
        <Form
            className="search-form"
            onSubmit={onSubmit}
            >
            <div className="input-group">
                <span className="input-group-text rubik-font">
                    <i className="icon ion-search"></i>
                </span>
                <input
                    className="form-control rubik-font"
                    type="text"
                    placeholder="Search for companies that start with..."
                    onChange={onChange}
                    value={value}
                />
                <Button className="btn btn-light rubik-font" type="submit">Search </Button>
            </div>
        </Form>
    )

}

CompanyList component source code: CompanyList组件源码:

function MapDataToCompanyList(response) {
    console.log(response); //Logging occurs here
    if(!response || response===undefined || response.length===0)
    {
        return (<ErrorBoundary message={noCompaniesError.message}></ErrorBoundary>)
    }
    return response.map((company) => {
        return (
            <Col key={company._id} xs={12} md={6} lg={4} className="mt-2">
                <CompanyCard
                    id={company._id}
                    logo={company.logo}
                    title={company.name}
                    logoBackground={company.logoBackground}
                    progLangs={company.progLangs}
                    backend={company.backend}
                    frontend={company.frontend}
                    url={company.url}
                >
                </CompanyCard>
            </Col>
        )
    })
}
const CompanyList = (props) => {
    const {companies} = props
    return (
        <div>
            <Container className="mt-3">
                <Row>
                    {
                    MapDataToCompanyList(companies)
                    }
                </Row>
            </Container>
        </div>
    )
}

export default CompanyList;

FetchSearchData function source code: FetchSearchData function 源代码:

export const fetchSearchData = (query, cb)=>{
    const uri = process.env.NODE_ENV === 'development' ?
    `http://localhost:3000/api/companies/name/${query}` :
    ``;
    axios.get(uri, {
        timeout: MAX_TIMEOUT
    })
    .then((response)=>{
        cb(response.data.data)
    })
    .catch((error)=>{
        console.log(error)
    })
}

重新渲染问题的屏幕截图

As seen above, empty list of companies is logged when the page first loads, then I typed three characters and the it logged three time which means the map function called three times.如上所示,首次加载页面时会记录空公司列表,然后我输入了三个字符,它记录了三次,这意味着 map function 被调用了三次。

在此处输入图像描述

Even then if I pressed submit and retrieved list of companies normally, whenever I type it will keep printing the array of companies that was fetched.即使那样,即使我按提交并正常检索公司列表,每当我键入时,它都会继续打印已获取的公司数组。

Sorry if I missed something, I am still new to React.抱歉,如果我错过了什么,我还是 React 的新手。

I don't get why CompanyList re-renders.我不明白为什么 CompanyList 会重新渲染。

Because it's nested in your Search component, and it's not React.memo 'd (or a PureComponent ).因为它嵌套在您的 Search 组件中,而不是React.memo (或PureComponent )。

Yes, the component is updated, but that doesn't mean it necessarily causes a DOM reconciliation.是的,组件已更新,但这并不意味着它一定会导致 DOM 协调。

In any case, React is completely at liberty of calling your component function as many times as it likes (and indeed, in Strict Mode it tends to call them twice per update to make sure you're not doing silly things), so you should look at side effects (such as console logging) in your component function (which you shouldn't have in the first place) as performance guidelines.无论如何,React 完全可以随意调用你的组件 function (实际上,在严格模式下,它倾向于每次更新调用它们两次以确保你没有做愚蠢的事情),所以你应该查看组件 function (一开始就不应该有)中的副作用(例如控制台日志记录)作为性能指南。

When you call setSearchInput(e.target.value) , Search component will re-render cause its state has changed.当您调用setSearchInput(e.target.value)时, Search组件将重新渲染,因为它的 state 已更改。 Search component re-renders means every tag nested in it will re-render (except the ones passed via children ). Search组件重新渲染意味着嵌套在其中的每个标签都将重新渲染(通过children传递的标签除外)。 That is the normal behaviour of React.这是 React 的正常行为。 If you want to avoid that, you would wanna use React.memo for CompanyList .如果你想避免这种情况,你会想要使用React.memo作为CompanyList Or you could use useRef to bind the input like so:或者您可以使用useRef来绑定input ,如下所示:

const Search = () => {
  const [companies, setCompanies] = useState([]); //List of companies returned from searching
  const inputRef = React.useRef(null);
  //use the API providing it the search input, and
  //setCompanies hook to update list of companies
  const onSearchSubmit = (e) => {
    e.preventDefault();
    fetchSearchData(inputRef.current.value, setCompanies);
    inputRef.current.value = "";
  };

  return (
    <div>
      <Container>
        <Row className={"searchFilterBar"}>
          <Col sm={6} md={8} className={"searchBar"}>
            <SearchBar inputRef={inputRef} onSubmit={onSearchSubmit} />
          </Col>
          <Col sm={6} md={4} className={"filterBar"}></Col>
        </Row>
        <CompanyList companies={companies}></CompanyList>
        <Row></Row>
      </Container>
    </div>
  );
};
export default Search;
const SearchBar = ({ onSubmit, inputRef }) => {
  return (
    <Form className="search-form" onSubmit={onSubmit}>
      <div className="input-group">
        <span className="input-group-text rubik-font">
          <i className="icon ion-search"></i>
        </span>
        <input
          ref={inputRef}
          className="form-control rubik-font"
          type="text"
          placeholder="Search for companies that start with..."
        />
        <Button className="btn btn-light rubik-font" type="submit">
          Search
        </Button>
      </div>
    </Form>
  );
};

You do not need to maintain a state for input field.您不需要为输入字段维护 state。 You can use useRef and pass it to input like below.您可以使用useRef并将其传递给输入,如下所示。

<input
  ref={inputRef}
  className="form-control rubik-font"
  type="text"
  placeholder="Search for companies that start with..."
/>

And you can get get value inside onSearchSubmit using inputRef.current.value您可以使用onSearchSubmitinputRef.current.value中获取值

This will not re-render you component on input change.这不会在输入更改时重新渲染您的组件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM