🔥 React 组件优化第二弹来袭!!

1,558 阅读4分钟

本片文章文章是继 避免React组件重复渲染的手段 这篇文章的后续,也就是第二弹!如果你还没有看过第一篇文章,我墙裂推荐你看一下!!

如果你觉得本片文章对你有所帮助,请不要吝啬你的 赞👍 哦!你的认可是我持续写作的最大动力!谢谢!

🤟 useRef 的其他用法

假如有一个这样的情景,要保存 input 输入的上次的值 previousValue 你要如何做呢? 也许你会这样写:

// 保存 之前输入的inputValue
// Save the previously entered inputValue
let pervValue: string = "";
const UseRefOpt = () => {
  const [inputValue, setInputValue] = useState<string>("hello");
  const handelInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setInputValue((oldValue: string) => {
      pervValue = oldValue;
      return e.target.value;
    });
  };
  useEffect(() => {
    console.log("🚀 inputValue:", inputValue, "🚀 pervValue:", pervValue);
  }, [inputValue]);
  return (
    <div>
      {/* <h1>UseRef multifunctional use</h1> */}
      <h1>UseRef 多种情况使用</h1>
      <input type="text" value={inputValue} onChange={handelInputChange} />
    </div>
  );
};

OK,这样写虽然也能解决问题,但是你有没有发现一个问题?当组件卸载之后重新加载完成,pervValue 还是初始的 空字符串 吗?

查看预览:

2021-11-17 08.49.14.gif

那或许你会说:我卸载的时候 重新 初始化不就好了? 例如这样:

useEffect(() => {
    console.log("🚀 inputValue:", inputValue, "🚀 pervValue:", pervValue);
    return () => {
      pervValue = "";
    };
  }, [inputValue]);

好的,没问题!如果说这个组件要使用多次呢?这种写法还支持吗?

📣 当这个组件使用多次时,在内存中只会有一个名字叫做 pervValue 的变量,所有的组件的值都会保存到这个变量当中,会造成保存的值错乱不准确!

所以这种写法是不合适的。那应该怎么写呢?使用 useState ?,不!!对于这种不必要的值,不需要声明为状态,让它造成页面的重复渲染。 因此这种情况下我们可以使用 useRef 。 那为什么要使用它呢?

  • 👏 useRef 返回值的 current 属性被改变时不会引发组件的重新渲染。
  • 👏 当组件卸载时,不需要重新初始化 useRef的值。
  • 👏 因为 useRef 的返回值是一个对象,因此我们使用时类似在使用类组件的 this 一样。

例如:

const UseRefOpt2 = () => {
  const [inputValue, setInputValue] = useState<string>("hello");
  const pervValueRef = useRef<string>("");
  const handelInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setInputValue((oldValue: string) => {
      pervValueRef.current = oldValue;
      return e.target.value;
    });
  };
  useEffect(() => {
    console.log("🚀 inputValue:", inputValue, "🚀 pervValueRef:", pervValueRef);
  }, [inputValue, pervValueRef]);
  return (
    <div>
      {/* <h1>UseRef multifunctional use</h1> */}
      <h1>UseRef 多种情况使用</h1>
      <input type="text" value={inputValue} onChange={handelInputChange} />
    </div>
  );
};

如此我们便完成了这个需求。

查看预览:

2021-11-17 08.51.24.gif

🤟 context 的 provider 优化

目前对于 React 状态管理的包在社区非常丰富。但是对于一些体量小的项目来说,如果去单独下载一个状态管理包,又用不了几次,无疑是划不来的!因此也有很多人选择 Context API + useReducer 来对项目的状态进行管理!

但是随着项目状态的增多,关注点的分离,不清楚你们的项目中 provider 是否存在下面的写法呢?

const ProvidersOpt = () => {
  return (
    <Context5.Provider value="Context5">
      <Context4.Provider value="Context4">
        <Context3.Provider value="Context3">
          <Context2.Provider value="Context2">
            <Context1.Provider value="Context1">
              <div>
                {/* <h1>Providers Optimization</h1> */}
                <h1>Providers 优化</h1>
              </div>
            </Context1.Provider>
          </Context2.Provider>
        </Context3.Provider>
      </Context4.Provider>
    </Context5.Provider>
  );
};

一眼扫过去这不是 地狱回调 吗?

因此对于上面的写法如果我们可以将这个 Providers 拉平是不是会好一点呢?

因此我们可以写一个方法,实现这个功能!例如: 参照 Redux 的写法

// 📣 value 这个参数是结合我的例子写的,因此你们在写的时候看自己情况!!
function providerOpt(...providers: React.Provider<string>[]) {
  return ({ children, value }: { children: JSX.Element; value: string[] }) =>
    providers.reduce(
      (prev, Provider, index) => (
        <Provider value={value[index]}>{prev}</Provider>
      ),
      children
    );
}

那如何使用呢?

const OptProvider = providerOpt(
  Context1.Provider,
  Context2.Provider,
  Context3.Provider,
  Context4.Provider,
  Context5.Provider
);

const ProvidersOpt2 = () => {
  return (
    <OptProvider
      value={["Context1", "Context2", "Context3", "Context4", "Context5"]}
    >
      {/* <h1>Providers Optimization</h1> */}
      <h1>Providers 优化</h1>
    </OptProvider>
  );
};

这个写法主要是运用了 reduce 的特性,把 children 累加到新的 Provider 中,最终的呈现的效果和 之前的 地狱回调的 写法一样,只是在视觉我们看不到这个地狱回调罢了!

👇🏻 点击查看源码。

👏👏👏 往期精彩