如何在 state 和 props 更改时渲染组件?

IT技术 javascript reactjs react-hooks
2021-05-07 22:27:59

我需要显示props值(这是一个简单的字符串)。每次我得到新的搜索结果时,我都会发送props.
在第一次渲染时,props总是undefined.

编辑:
Header.jsx

function Header() {
  const [searchString, setString] = useState('');
      
  const onChangHandler = (e) => {
    setString(e.target.value);
  };
  
  const activeSearch = () => {
    if (searchString.length > 0) {
      <Home searchResults={searchString} />;
    }
  };

  return (
    <div>
        <input
          placeholder='Search here'
          value={searchString}
          onChange={(e) => onChangHandler(e)}
        />
        <button onClick={activeSearch}>Search</button>
      </header>
    </div>
  );
}

我搜索了以前的stackoverflow问题和reactjs.org,但没有找到答案。

主页.jsx

import React, { useEffect, useState } from 'react';

function Home({ searchResults }) {
  const [itemSearchResults, setResults] = useState([]);
  const [previousValue, setPreviousValue] = useState();

  // What function will re-render when the props are first defined or changed ? 
   useEffect(() => { // Doesn't work
    setResults(searchResults);
   }, [searchResults]);
           
  return (
    <div>
      <h3>Home</h3>
      <h1>{itemSearchResults}</h1>
    </div>
  );
}

export default Home;

应用程序.js

function App() {
  return (
    <div className='App'>
      <Header />
      <Home />
      <Footer />
    </div>
  );
}

我发送输入字符串只是为了检查props子组件(“Home”)是否会改变。

这里有没有高手知道是什么问题?

2个回答

为什么它不起作用?

这是因为该Home组件从未使用过,即使它包含在以下代码段中:

const activeSearch = () => {
  if (searchString.length > 0) {
    <Home searchResults={searchString} />;
  }
};

activeSearch函数有几个问题:

  • 尽管它使用 JSX (在渲染阶段之外),但它被用作事件处理程序
  • 它不返回 JSX (在渲染阶段仍然会失败)

JSX 应该只在 React 生命周期的渲染阶段使用。任何事件处理程序都存在于这个阶段之外,所以它可能使用的任何 JSX 都不会出现在最终的树中。

数据决定了要呈现的内容

也就是说,解决方案是使用状态来知道在渲染阶段要渲染什么。

function Header() {
  const [searchString, setString] = useState('');
  const [showResults, setShowResults] = useState(false);

  const onChangHandler = (e) => {
    // to avoid fetching results for every character change.
    setShowResults(false);
    setString(e.target.value);
  };

  const activeSearch = () => setShowResults(searchString.length > 0);

  return (
    <div>
        <input
          value={searchString}
          onChange={(e) => onChangHandler(e)}
        />
        <button onClick={activeSearch}>Search</button>
        {showResults && <Home query={searchString} />}
    </div>
  );
}

useEffect 根据props变化触发效果

然后,该Home组件可以通过 触发对某些服务的新搜索请求useEffect

function Home({ query }) {
  const [results, setResults] = useState(null);

  useEffect(() => {
    let discardResult = false;

    fetchResults(query).then((response) => !discardResult && setResults(response));

    // This returned function will run before the query changes and on unmount.
    return () => {
      // Prevents a race-condition where the results from a previous slow
      // request could override the loading state or the latest results from
      // a faster request.
      discardResult = true;

      // Reset the results state whenever the query changes.
      setResults(null);
    }
  }, [query]);

  return results ? (
    <ul>{results.map((result) => <li>{result}</li>))}</ul>
  ) : `Loading...`;
}

确实,useEffect文章重点介绍的那样某些状态与props同步并不是最佳选择

useEffect(() => {
  setInternalState(externalState);
}, [externalState]);

...但在我们的例子中,我们没有同步状态,我们实际上是在触发一个效果(获取结果),这正是为什么useEffect存在的原因


更好的解决方案:不受控制的输入

在您的情况下,另一种技术是使用不受控制<input>的 aref并且仅在单击按钮而不是更改输入值时更新搜索字符串。

function Header() {
  const [searchString, setString] = useState('');
  const inputRef = useRef();

  const activeSearch = () => {
    setString(inputRef.current.value);
  }

  return (
    <div>
        <input ref={inputRef} />
        <button onClick={activeSearch}>Search</button>
        {searchString.length > 0 && <Home query={searchString} />}
    </div>
  );
}


传递状态

[下一行] 将Home组件引入组件内部Header,这使得重复

 {searchString.length > 0 && <Home query={searchString} />}

为了使Header组件可重用,最快的方法是提升状态

// No state needed in this component, we now receive
// a callback function instead.
function Header({ onSubmit }) {
  const inputRef = useRef();

  const activeSearch = () => {
    // Uses the callback function instead of a state setter.
    onSubmit(inputRef.current.value);
  }

  return (
    <div>
        <input ref={inputRef} />
        <button onClick={activeSearch}>Search</button>
    </div>
  );
}

function App() {
  // State lifted up to the parent (App) component.
  const [searchString, setString] = useState('');

  return (
    <div className='App'>
       <Header onSubmit={setString} />
       {searchString.length > 0 && <Home query={searchString} />}
       <Footer />
    </div> 
  );
}

如果该解决方案仍然太有限,还有其他方法可以传递数据,而这些数据将是题外话,以便在此答案中将它们全部提出,因此我将链接更多信息:

如果您的props作为搜索结果传递,则将props更改为,

function Home({ searchResults}) {...}

并使用

useEffect(() => { // code, function },[searchResults]) ).