发布于 2026-01-06 2 阅读
0

少即是多;简化你的 React 代码,让你的应用性能更强大——第一部分

少即是多;简化你的 React 代码,让你的应用性能更强大——第一部分

React开发中,我们开发者常常会忘记一条准则,但这条准则却永远不应该被遗忘:“少即是多”。它不仅仅是一句格言,更是一种思维方式、一种解决问题的方法,它应该影响你的设计。KISS原则已经存在了 60 多年,时至今日,它依然像半个多世纪前那样具有前瞻性。

作为开发者,我们应该避免过度设计和过度开发,即在只需较少工作量即可达到相同结果的情况下,却投入过多精力。这可以通过多种方式实现,例如将组件重构为更小的组件、降低组件输入/输出的复杂性,以及避免过多的处理和复杂的算法。

我们应该力求一切尽可能简单,但又不能过于简单。也就是说,我们要在不造成认知负荷的情况下尽可能高效地工作,而不是把工作简化到极致,反而增加工作量。这并非新手程序员独有的问题;我们都曾为了达成目标而选择过捷径。有时我们别无选择,有时是因为不知道更好的方法,有时则只是因为我们不想投入时间认真去做。

“少即是多”的理念适用于各种经验水平的开发者,而且应该践行。它必然会提升你的应用程序开发水平,改进你参与开发的应用程序,并帮助你更高效地工作。衡量开发者的最终目标不应该是代码行数,而应该是代码质量、错误率和返工率。

简化 React 组件

我们可以采取多种策略来简化组件,而无需对现有组件进行大刀阔斧的改造。每一种策略都将在不同的博客文章中进行介绍。

  1. 将状态与显示分离,这将有助于您的应用程序符合成熟的 MVC 规则。
  2. 将处理延迟到服务和自定义钩子
  3. 避免超负荷运转useEffectuseState
  4. 确定是否redux真的redux-saga需要
  5. 创建更高阶的组件来连接组件之间的功能
  6. 将计算逻辑从组件中移到辅助函数中,并通过自定义钩子注入。
  7. 尽可能使用延迟加载和延迟行为

1. 将状态与显示分离,这将有助于您的应用程序符合成熟的 MVC 规则。

遵循MVC原则的传统应用程序设计,会将应用程序逻辑拆分为三个不同的组件:模型(Model)、视图(View)和控制器(Controller)。控制器负责处理用户进入和退出以及用户事件。模型负责响应用户数据的变化,而视图则始终反映模型的内容。

MVC模型

让我们来看一个简化常见 React 组件结构的例子:

const globalState = someStateTool();
const myComponent: React.FC<> = () => {
  const [ myState, setMyState ] = useState<any>({});
  const [ loaded, setLoaded ] = useState<boolean>(false);

  useEffect(() => {
    setTimeout(() => { setLoaded(true); }, 2500);
    setTimeout(() => { globalState.set("foo", "bar")}, 5000);
  }, [])

  return loaded ? (<MySubComponent/>) : (<SpinnerComponent/>);
}

const mySubComponent: React.FC = () => {
  const [ someState, setSomeState ] = useState<any>(null);
  globalState.subscribeTo("someEvent", ev => setSomeState(ev.data));
  const handleClick = () => globalState.set("foo", "bar");

  return (
    <div>
      <button onClick={handleClick}>Some title</button>
    </div>
    <div>{someState.foo}</div>
  )
}
Enter fullscreen mode Exit fullscreen mode

每个组件都包含各自独立的功能。因此,它们并非纯粹的组件,而是独立且可互换的。这类组件本身就能响应各种用户输入行为和数据驱动事件。这通常会导致复杂性和耦合性的增加,即使不是直接体现在父组件上,而是体现在数据流、事件订阅以及其他数据和事件源上。

每个组件都需要进行大量的测试工作,因为它们都需要模拟各种服务和提供商,并处理行为和交互。

// Create a contract for the sub component
type SubComponentType = { foo: string, handleClick: () => void };

const globalState = someStateTool();
const myComponent: React.FC<> = () => {
  const [ myState, setMyState ] = useState<any>({});
  const [ loaded, setLoaded ] = useState<boolean>(false);
  globalState.subscribeTo("someEvent", ev => setMyState(ev.data));
  const handleClick = () => globalState.set("foo", "bar");

  useEffect(() => {
    setTimeout(() => { setLoaded(true); }, 2500);
    setTimeout(() => { globalState.set("foo", "bar")}, 5000);
  }, [])

  return loaded ? (<MySubComponent foo={myState.foo} handleClick={handleClick}/>) : (<SpinnerComponent/>);
}

// Make sure our component adheres to the type contract
const mySubComponent: React.FC<SubComponentType> = ({ foo, handleClick }) => {
  return (
    <div>
      <button onClick={handleClick}>Some title</button>
    </div>
    <div>{foo}</div>
  )
};
Enter fullscreen mode Exit fullscreen mode

我们甚至可以更进一步,将过渡组件分离成更高阶的组件,或者说是一个包装组件,根据状态渲染不同的组件。

type SubComponentType = { foo: string, handleClick: () => void };

const globalState = someStateTool();

const myComponentLoader: React.FC = () => {
  const [ loaded, setLoaded ] = useState<boolean>(false);

  useEffect(() => {
    setTimeout(() => { setLoaded(true); }, 2500);
  }, [])

  return loaded ? (<MyComponent/>) : (<SpinnerComponent/>);
}

const myComponent: React.FC<> = () => {
  const [ myState, setMyState ] = useState<any>({foo: globalState.get("foo")});
  globalState.subscribeTo("someEvent", ev => setMyState(ev.data));
  const handleClick = () => globalState.set("foo", "bar");

  return <MySubComponent foo={myState.foo} handleClick={handleClick}/>;
}

const mySubComponent: React.FC<SubComponentType> = ({ foo, handleClick }) => {
  return (
    <div>
      <button onClick={handleClick}>Some title</button>
    </div>
    <div>{foo}</div>
  )
};
Enter fullscreen mode Exit fullscreen mode

我们编写了更多行代码来表示相同的组件结构,但是:

  1. 将模型逻辑与视图逻辑分离
  2. MySubComponent是一个纯函数;在相同的输入条件下,它应该始终产生相同的输出。
  3. MyComponent使用 Enzyme 之类的工具可以轻松进行测试——只需验证子组件是否已加载即可。
  4. 所有加载逻辑都由一个顶层组件处理。可加载的组件可以根据需要进行切换。

敬请期待第二部分,我将在其中介绍将处理延迟到服务和自定义钩子

文章来源:https://dev.to/jmitchell38488/less-is-more-simplify-your-react-code-to-super-power-your-applications-part-1-2ga2