这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战。
以结果为导向,写给刚学完前端三剑客和想要了解 React 框架的小伙伴,使得他们能快速上手(省略了历史以及一些不必要的介绍)。
props 处理
我们都知道 props 是一个 JS 对象,所以在传递时我们可以对它应用一些 JS 技巧,比如对象解构来访问其属性。
- 与数组解构类似,举个例子:
const user = {
firstName: 'Man',
lastName: 'cuoj',
};
// 没用对象解构
const firstName = user.firstName;
const lastName = user.lastName;
// 用了
const { firstName, lastName } = user;
- 实际应用,直接在函数签名里对 props 对象进行解构:
// 相当于在函数内部定义 const { onSearch, searchTerm } = props;
const Search = ({ onSearch, searchTerm }) => {
return (
<>
<label htmlFor="search">Search: </label>
<input
id="search"
type="text"
onChange={onSearch}
value={searchTerm}
/>
<p>
Searching for <strong>{searchTerm}</strong>.
</p>
</>
);
};
在 React 应用中,我们经常使用包含在 props 对象中的信息,而很少直接用到 props 对象。通过在函数签名中解构 props 对象,我们可以重构 List 组件,并从中提取出一个新的 Item 组件:
const List = ({ list }) =>
list.map((item) => <Item key={item.objectID} item={item} />);
const Item = ({ item }) => (
<div>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
</div>
);
可以看到 Item 组件中的 item 对象也没有被直接使用过,所以我们还可以采用【嵌套解构】的方式直接收集所有需要 item 对象的信息。
- 举个例子:
const user = {
firstName: 'Man',
pet: {
name: 'dd',
},
};
// 嵌套解构对象
const {
firstName,
pet: {
name,
},
} = user;
- 实际应用:
const Item = ({ item: { url, title, author } }) => (
<div>
<span>
<a href={url}>{title}</a>
</span>
<span>{author}</span>
</div>
);
或者我们直接把 item 对象的每一个属性传给 Item 组件,而不再传递 item 对象:
const List = ({ list }) =>
list.map((item) => (
<Item
key={item.objectID}
title={item.title}
url={item.url}
author={item.author}
/>
));
const Item = ({ url, title, author }) => (
<div>
<span>
<a href={url}>{title}</a>
</span>
<span>{author}</span>
</div>
);
我们还可以通过 JS 的展开语法将对象的【所有键-值对】作为【属性-值对】传递给 JSX,从而使传递过程更加简洁:
const List = ({ list }) =>
list.map((item) => <Item key={item.objectID} {...item} />);
继续通过 JS 的剩余参数把只用作 key 的 objectID
从 item 中分离出来,剩下的项作为属性-值对传递给 Item 组件:
const List = ({ list }) =>
list.map(({ objectID, ...item }) => <Item key={item.objectID} {...item} />);
尽管这两个的语法都是三个点,但你不应该混淆它们。剩余运算符一般发生在解构的右侧,用于将一个对象和它的某些属性分开,展开运算符发生在左侧,用于展开对象的所有键值对。
虽然这样重构会使函数体变得很简洁,但是【可读性】会变得很差,毕竟展开运算符和剩余运算符并不是每个人都熟悉的,我们还是用回最初的版本:
const List = ({ list }) =>
list.map((item) => <Item key={item.objectID} item={item} />);
const Item = ({ item }) => (
<div>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
</div>
);
React 副作用
我们可以通过 localStorage
给 Search 组件添加一个新功能:当用户输入然后刷新浏览器标签页时,让浏览器记住最后一个搜索项。
const App = () => {
...
const [searchTerm, setSearchTerm] = React.useState(
localStorage.getItem("search") || "React"
);
const handleSearch = (e) => {
setSearchTerm(e.target.value);
localStorage.setItem("search", e.target.value);
};
...
};
不过处理函数应该只关心更新 state 的问题,但现在我们跨过了 React 的领域和浏览器的 API 互动产生了副作用,这是我们不能接受的。
所以这里我们引入 useEffect hook 在每次更新 state 即 searchTerm
发生改变时来触发副作用:
const App = () => {
...
const [searchTerm, setSearchTerm] = React.useState(
localStorage.getItem("search") || "React"
);
React.useEffect(() => {
localStorage.setItem("search", searchTerm);
}, [searchTerm]);
const handleSearch = (e) => {
setSearchTerm(e.target.value);
};
...
};
可以看到 useEffect hook 需要两个参数:
- 第一个参数是会产生副作用的函数
- 第二个参数其依赖的变量【数组】
如果任何一个依赖的变量发生改变,包含副作用的函数就会被调用。对于我们来说,当组件第一次渲染时,它会初始化调用一次,之后每次 searchTerm
的改变都会调用这个函数。
如果 useEffect 的依赖数组是个空数组,那么副作用函数只会在组件第一次渲染时被调用一次。
专栏
因为参加打卡活动是每日更新,所以可能比较短小,可以关注一下 React 入门专栏。
在更新完后会整合为一整篇,感谢关注和点赞!