最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React Hooks 使用实践 | 七日打卡

    正文概述 掘金(一个卷er)   2021-01-15   464
    React Hooks 使用实践 | 七日打卡

    前言

    目前来说,每个前端团队都有自己对 React Hooks 的使用偏好,在基于 hooks 的数据流管理或许没有所谓的最佳实践,但是开发过程中还是有一些准则可以参考。当你不熟悉一个新东西的时候,你就该去看它的文档。

    React Hooks 是完全不同以往的心智模型,从使用开始我们就不应该以之前的思维去强行套用。要理解 Function Component 的最大特点就是更加彻底的状态驱动,虽然有很多文章写了如何用 Hooks 模拟生命周期,但是其实这里生命周期的概念早已名存实亡。

    在实际使用中,我们只要牢牢记住“状态/流”的思想,所有的事件、请求和数据变化都依赖于该作用的“输入流”。把握住这点,我们再去考虑数据流变化后的触发效果,考虑作用是否需要触发?“输入流”是否可变?想清楚后就放心大胆地写吧。

    流的思想

    怎么通过流的思想去理解 Hooks 的使用呢?这里我们可以举几个例子:

    逝者如斯夫,不舍昼夜

    props 和 state 都是不可变的常量(const)!每次 render 的时候,Function Component 相当于重新执行了一遍。每次都是新的 state,新的 effect,历史的状态则被保留在 React 的 “快照”中 。这里不再是this.statetihs能够保留不变性,多次修改;而是可以理解成多次渲染生成多份状态。

    正如官方文档的这个例子,不指定依赖的情况下,effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行当前 effect 之前对上一个 effect 进行清除。

    function FriendStatus(props) {
      const [isOnline, setIsOnline] = useState(null);
    
      useEffect(() => {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        // Specify how to clean up after this effect:
        return function cleanup() {
          ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
      });
    
      if (isOnline === null) {
        return 'Loading...';
      }
      return isOnline ? 'Online' : 'Offline';
    }
    

    水之形,避高而趋下

    正如水流只能从上游流向下游,在 React 中,状态也是单向的。因此,对于跨组件数据共享,由于数据只能从组件树的父节点传递给子节点,需要我们提供特殊的策略,比如 Context。

    const ThemeContext = React.createContext(themes.default);
    // 生产者
    function App() {
      const [color, setColor] = useState(themes.light);
      return (
        <ThemeContext.Provider value={color}>
          <Toolbar1 />
          <Toolbar2 />
        </ThemeContext.Provider>
      );
    }
    // 消费者
    function Toolbar(props) {
      const theme = useContext(ThemeContext);
      return (
        <div>
        	<button style={{ background: theme.background, color: theme.foreground }}>
            I am styled by theme context!
        	</button>
        </div>
      );
    }
    

    水是有流向性的,hooks 的也依赖于调用的顺序,我们可以把一个 state 作为参数传递给下一个 hooks。

    function ChatRecipientPicker() {
      const [recipientID, setRecipientID] = useState(1);
      const isRecipientOnline = useFriendStatus(recipientID);
    
      return (
      //...
      )
    }
    

    流的实践

    其实用 hooks 在一些应用场景中,可以实现很简洁的写法。比如请求一个 List,我们把“分页”、“查询”、“重刷”都当作状态来处理,请求操作只要依赖这些状态即可。这样看来,这里的用法其实很像 Rxjs 的combineLatest

    const App = () => {
      const [pagination, setPagination] = useState<object>(defaultPagination);
      const [searchValue, setSearchValue] = useState<object>(defaultSearchValue);
      const [refresh, setRefresh] = useState<number>(0);
      useEffect(() => {
        // fetch
      }, [pagination, searchValue, refresh])
    };
    

    Hooks 优化实践

    个人实践总结,hooks 的优化一般可以从两个方面入手:

    • 一是通过比较,需要的时候才进行渲染
    • 二是使用不依赖/不变量跳过渲染

    compare 优化

    由于父组件每次 render 都可能引起 props 的改变,因此传递的值尽量使用 useMemo 或者 useCallback 优化。

    • useCompareEffect
    // https://github.com/kentcdodds/use-deep-compare-effect
    type Effect = typeof React.useEffect;
    type EffectDeps = Parameters<Effect>[1];
    
    export const useDeepCompareEffect: Effect = (effect, deps?) => {
      const prevRef = useRef<EffectDeps>();
      const deepDep = useRef<number>(0);
      if (!deepEqual(prevRef.current, deps)) {
        prevRef.current = deps;
        deepDep.current = deepDep.current + 1;
      }
      // eslint-disable-next-line react-hooks/exhaustive-deps
      return useEffect(effect, [deepDep.current]);
    };
    
    • useDeepCompareMemo
    type Memo = typeof React.useMemo;
    type MemoDeps = Parameters<Memo>[1];
    
    export const useDeepCompareMemo: Memo = (factory, deps?) => {
      const prevRef = useRef<MemoDeps>();
      const deepDep = useRef<number>(0);
      if (!deepEqual(prevRef.current, deps)) {
        prevRef.current = deps;
        deepDep.current = deepDep.current + 1;
      }
      // eslint-disable-next-line react-hooks/exhaustive-deps
      return useMemo(factory, [deepDep.current]);
    };
    
    • usePrevious

    如果我们想拿到上次渲染的状态,再进行比较,可以用usePrevious

    // https://github.com/streamich/react-use
    export default function usePrevious<T>(state: T): T | undefined {
      const ref = useRef<T>();
      useEffect(() => {
        ref.current = state;
      });
      return ref.current;
    }
    
    // compare
    const App = () => {
      const [count, setCount] = useState<number>(0);
      const prevCount = usePrevious<number>(count);
      useEffect(() => {
        if(count > prevCount + 5) {
          //...
        }
      }, [count])
    }
    

    不依赖/不变量

    • defaultProps

    对于不变的默认参数,我们可以写在函数以外

    const defaultValue = { status: "OPEN" };
    const App = ({ value = defaultValue }) => {
    	//...
    };
    // or
    App.defaultProps = {
      value: { status: "OPEN" }
    }
    
    • useRef

    对于某个数据,如果不需要被依赖又要保证最新的值可以保存在ref

    const App = ({ onChange }) => {
      const callback = useRef(onChange);
      callback.current = onChange;
      useEffect(() => {
        //...
        dom.addEventListener("click", callback.current);
        //...
      }, [])
    };
    
    • 函数式 setState
    const App = () => {
      const [count, setCount] = useState<number>(0);
      useEffect(() => {
      	setCount(count => count + 1);
      }, [])
    };
    

    总结

    主要还是讲了个人的一些实践经验,前端数据流这部分还有很多内容需要理解和学习,先做下记录吧~

    参考文章


    起源地下载网 » React Hooks 使用实践 | 七日打卡

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元