以结果为导向,写给刚学完前端三剑客和想要了解 React 框架的小伙伴,使得他们能快速上手(省略了历史以及一些不必要的介绍)。
状态提升
虽然我们创建了一个回调函数来将信息向上共享,但我们的目标是在多个组件中共享 Search 组件的 state。
在将 stories 传递给 List 组件之前,我们需要在 App 组件中【使用用户输入来过滤列表】,所以 state 要从子组件 Search 提升到父组件 App,以便更多组件共享 state。
也就是说 Search 组件不再管理 state,只需要在用户输入后,将事件向上传递给父组件 App:
const App = () => { const stories = [ ... ]; // 状态提升 const [searchTerm, setSearchTerm] = React.useState(""); const handleSearch = (e) => { setSearchTerm(e.target.value); }; return ( <div> <Search onSearch={handleSearch} /> <hr /> <List list={stories} /> </div> ); }; const Search = (props) => ( <div> <label htmlFor="search">Search: </label> <input id="search" type="text" onChange={props.onSearch} /> </div> ); 复制代码
当然你也可以将 searchTerm
作为 props 向下传递,继续在 Search 组件中展示:
const App = () => { ... return ( <div> <Search onSearch={handleSearch} searchTerm={searchTerm}/> ... </div> ); }; const Search = (props) => ( <div> <label htmlFor="search">Search: </label> <input id="search" type="text" onChange={props.onSearch} /> <p> Searching for <strong>{props.searchTerm}</strong>. </p> </div> ); 复制代码
总之如果下游的组件要使用 state,可以将其作为 props 传递;如果下游组件要更新 state,可以向下传递一个回调处理函数。【可参考 Search 组件】
然后就是通过 searchTerm
值 ,使用 JS内置的数组过滤函数 来过滤列表,同时做了【大小写不敏感】的匹配:
const App = () => { ... const searchedStories = stories.filter((story) => story.title.toLowerCase().includes(searchTerm.toLowerCase()) ); return ( <div> <Search onSearch={handleSearch} searchTerm={searchTerm} /> <hr /> <List list={searchedStories} /> </div> ); }; 复制代码
Array.filter()
接收一个函数作为参数,并返回一个全新的数组。
该函数遍历数组的每一项并返回 true 或 false,如果返回 true,则代表该项满足条件,会保存到新数组中,反之则不保存,遍历完成后返回新数组。
现在你已经可以在输入框输入来进行搜索了,通过 Search 组件的回调处理函数,我们在 App 组件中更新了 state,并通过 state 过滤了列表传递给了 List 组件。
React fragment
你可能会注意到:当我们创建 Search 组件时,必须引入一个顶层的 HTML 元素把它包起来才能渲染:
const Search = (props) => ( <div> <label htmlFor="search">Search: </label> <input id="search" type="text" onChange={props.onSearch} /> <p> Searching for <strong>{props.searchTerm}</strong>. </p> </div> ); 复制代码
这是因为 React 组件返回的 JSX 虽然看起来像 HTML,但它在底层会被转换为【纯 JS 对象】,如果有多个同级的顶层元素,我们必须把它们放在数组中,而且加上 key 属性,以便从函数中返回多个对象:
// 举例 const Search = () => [ <input key="1" />, <input key="2" />, ]; 复制代码
当然我们有另一种简洁的解决方式,就是 React fragment:
const Search = (props) => ( <> <label htmlFor="search">Search: </label> <input id="search" type="text" onChange={props.onSearch} /> <p> Searching for <strong>{props.searchTerm}</strong>. </p> </> ); 复制代码
这个空的 tag 就是所谓的 fragment,像这样替换掉 div 就可以让你在 HTML 树中【不留痕迹】地对事物进行分组了。