2022 React 最速上手指南(十)—— props 处理 & React 副作用

236 阅读3分钟

这是我参与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 入门专栏

在更新完后会整合为一整篇,感谢关注和点赞!