最前端|关于React的分享,内附React组件设计模式介绍

一、React介绍

对于有一定经验的前端开发者来说,学习 React 前了解一些它和其他框架的区别更有利于理解。React 和 Vue 有许多相似之处,比如他们都使用 Virtual DOM 对原生的 DOM API 进行了屏蔽,都提供了响应式的组件,都提倡单向数据流,但是他们也有很大不同:

首先 React 不应该叫框架,应该叫库,React 在设计理念中有明确表示一般而言拒绝添加开发者可以实现的特性;也就是说,与其说 React 是一个包含前端研发各方面述求的框架,不如简单地将它当做一个渲染函数的管理引擎,仅仅扮演了 MVC 架构模式中的 View 层。这也就导致了 React 更关注如何更高效地实现渲染,而不是上层应用解决方案。

因此,在不使用基于 React 的框架(Gatsby、Next.js等)的情况下,React 本身是缺乏研发标准的(也可以说 React 更加开放),这导致许多 React 初学者(包括作者本人)在学习完 JSX、Hook 等基本语法和 API 后依然会在解决实际项目中遇到的问题时为应该使用哪个 API、应该利用哪种 React 特性产生困惑。

下面分享几个非常实用的 React 组件设计的模式

二、React组件设计模式

(一)HOC模式

在理解 HOC 模式前需要先理解什么叫:横切关注点(Cross-Cutting Concerns)。横切关注点可以简单地理解为在多个不同的组件之间共享相同的功能,这些关注点通常代表了系统的通用需求或者说次要需求,比如权限、日志、数据转化等。

HOC 模式就是一种在不同场景下复用相同的处理逻辑的模式,HOC 是一个 JS 函数,它接受一个组件作为参数,添加额外的功能或者数据并且返回一个组件。

HOC 的结构类似:

const higherOrderComponent = (DecoratedComponent) => () => {
  const extraProps = {
    // ...
  }
  return <DecoratedComponent {...extraProps} />
}

许多流行的 React 框架用到了这一模式比如 Redux 中的 connect 和 react-router 中的 withRouter。

(二)展示型组件和容器组件模式

这一模式认为如果将组件分为两个类型(展示型和容器型),通过对关注点的分离可以更加容易对组件进行管理和复用。

通常我们认为 React 组件是由状态、状态的操作函数、UI(JSX)几个部分组成的,如果再细分一下,状态可以分为视觉性状态、交互性状态和数据性状态;

那么所谓的关注点分离,就是将 JSX、视觉性状态及视觉性操作函数放到展示型组件中,将交互性状态、数据性状态及其操作函数放到容器组件中;

那么对应地,展示型组件只需要接受和视觉性有关的参数,交互和数据相关的参数则由容器组件接收。

举个例子,展示型组件的结构类似:

const UsersList = ({ users }) => {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.username}</li>
      ))}
    </ul>
  )
}

容器组件的结构类似:

const Users = ({ children }) => {
  const [state, setState] = useState({})
  useEffect(() => {
    fetchUsers()
  }, [])
  return React.cloneElement(children, { ...state })
}

通过关注点的分离,除了 Users 这一容器组件外,其他的容器组件也可以复用 UserList 来展示数据,同样地,UserList 也可以用其他容器组件来提供数据。

注意:
在 React 16.8 版本后,使用 Hook 的组合替代容器组件通常可以减少很多冗余代码,使代码更加简洁;在能够直接复用 Hook 的情况下,开发一个容器组件已经没有必要,但是将数据性状态和交互性状态从展示型组件中分离依然很有价值。

(三)复合组件模式

这一模式提供了一种 在多个组件间共享状态、协同工作以完成一个共同目标 的有效方法。

比如:使用 Select 组件和 Option 组件实现下拉选择,结构类似于:

const SelectContext = createContext()

const Select = ({ children }) => {
  const [activeOption, setActiveOption] = useState()
  return (
    <SelectContext.Provider value={
   
   { activeOption, setActiveOption }}>
      {children}
    </SelectContext.Provider>
  )
}

const Option = ({ key, children }) => {
  const { activeOption, setActiveOption } = useContext(SelectContext)
  if (!activeOption || !setActiveOption) {
    throw new Error('Option 组件不能单独使用')
  }
  return (
    <div
      className={activeOption === key ? 'active' : ''}
      onClick={() => setActiveOption(key)}
    >
      {children}
    </div>
  )
}

Select.Option = Option

export default Select

在这个例子中,Select 和 Option 组件通过 Context API 共用了 activeOption 这一状态及其操作函数,并且 Option 组件是与 Select 组件高度耦合的,无法单独使用。

在使用复合组件模式时,对应客户端代码可以更加灵活,类似:

export default function App() {
  return (
    <Select>
      <Select.Option key='oliver'>Oliver</Select.Option>
      <Select.Option key='eve'>Eve</Select.Option>
    </Select>
  )
}

使用复合组件模式有几个好处:

  1. 父子组件间可以隐式进行交互,无需由组件外部传入参数控制交互;

  2. 引入 Select 组件会自动引入 Option 而无需手动引入;

  3. 声明式的 UI 组件更具有语义化。

React 的开发经验就分享到这里,希望这篇文章能够帮助你充分利用 React 的所有优势来创建可扩展、可维护的 React 应用程序,并在正确的位置应用正确的模式来优化你的项目。

猜你喜欢

转载自blog.csdn.net/CBGCampus/article/details/130082201