【问题标题】:Which is the better way to read Apollo reactive variables哪个是读取 Apollo 反应变量的更好方法
【发布时间】:2022-10-30 06:01:42
【问题描述】:

我正在关注一些关于使用反应变量作为状态管理的教程 react/apollo 客户端应用程序中的解决方案,我注意到有两种方法可以引用反应变量的当前值:

  • 使用钩子useReactiveVar - const myVar = useReactiveVar(myReactiveVar);
  • 或者只是调用不带参数的反应变量const myVar = myReactiveVar();

所以我的问题是:

使用一种方法来引用反应变量是否有好处,如果是这样,那为什么?

我有一个理论,即引用反应变量当前值的方式类似于使用基于当前状态设置状态的方式:

  • 我们可以直接引用状态 - setState(count + 1);
  • 或者我们传递一个函数 - setState((prev) => prev + 1)。 第二种方式被认为“更安全”,因为它保证在异步代码期间准确读取当前状态。我无法确定我的理论是否正确!

这是一个简单的组件,我使用两种方式,并且两者都在使用读取反应变量的当前值的两种情况下工作:

import React from 'react'
import { useQuery, useReactiveVar } from '@apollo/client';
import { missionsLimitRV } from '../../apollo/client';
import { GET_MISSIONS } from '../../data/queries';
 
export const Missions = () => {
  const limit = useReactiveVar(missionsLimitRV); <---here--<<

  const { data, loading } = useQuery(GET_MISSIONS, {
    variables: {
      limit: limit
    }
  });

  const addMission = () => {
    missionsLimitRV(missionsLimitRV() + 1) <---here-<<
  }

  if (loading) {
    return <h2>Loading...</h2>
  }

  if (!data.missions.length) {
    return <h2>No Missions Available</h2>
  }

  const missions = data.missions;
  console.log(missions);

  return (
  <div>
    <button onClick={addMission}>add mission</button>
    { missions.map((mission) => (
    <div key={mission.id}>
      <h2>{mission.name}</h2>
      <ul>
        {mission?.links?.map((link) => (
          <li key={link}><a href={link}>{link}</a></li>
        ))}
      </ul>
    </div>
  )) }
  </div>
  );
};

谢谢阅读! :)

【问题讨论】:

    标签: reactjs react-hooks apollo apollo-client


    【解决方案1】:

    刚刚遇到这个问题 - 我知道你问已经有一段时间了,但是这个概念也给我带来了很多困惑,所以希望这对某人有所帮助。这是我的看法:

    我认为原因与您的理论有点不同-当地国家问题是由于陈旧的关闭.反应变量原因与如何渲染在 React 中触发。

    陈旧的关闭问题

    这是您提到的反例的简单说明。

    const TimerLocalState = () => {
      const [count, setCount] = React.useState(0)
      
      const logCount = () => {
        console.info(count)
      }
    
      React.useEffect(() => {
        const id = setInterval(() => setCount(count + 1), 1000)
        
    
        return () => clearInterval(id)
      }, [])
    
      return (
      <>
        <h1>{count}</h1>
        <button onClick={logCount}/>
      </>)
    }
    
    }
    

    在此组件中,显示的计数器值将变为 1,然后保持在那里。原因是count 的值没有在执行setCount 的上下文中更新。同样,如果您按Log Count,记录的值将始终为 1。

    为了解决这个问题,我们有两个选择:

    1. 通过添加 count 作为依赖项来更新 useEffect 的上下文
        React.useEffect(() => {
          const id = setInterval(() => setCount(count + 1), 1000)
          
      
          return () => clearInterval(id)
        }, [count]) <--------- CHANGE 
      
      
      1. 将函数传递给setCount - 这使我们可以访问之前的状态参数
      React.useEffect(() => {
          const id = setInterval(() => setCount((prevCount) => prevCount + 1), 1000) <--------- CHANGE
          
      
          return () => clearInterval(id)
        }, [])  
      
      

      使用 Apollo,我们没有同样的问题,这就是为什么它在您的代码中可以双向工作,如果组件以编程方式引用该值.

      const countVar = makeVar(0)
      
      const TimerApolloState = () => {
        
        const logCountVar = () => {
          console.info(countVar())
        }
        
      
        React.useEffect(() => {
          const id = setInterval(() => {
            const newVal = countVar() + 1;
            countVar(newVal)
          }, 1000)
      
          return () => clearInterval(id)
        }, [])
      
        console.info("Render")
      
        return  (
        <>
          <h1>{countVar()}</h1>
          <button onClick={logCountVar}>Log CountVar</button>
        </>)
      }
      

      这是我们得到的:

      文本不会更新,因为组件仅在安装时呈现一次。但是如果我们点击Log CountVar 按钮,组件仍然可以访问最新的值。

      因此,使用 countVar() 可以在像您这样的示例中以编程方式引用 store 变量。如果您需要引用 store 变量的最新值但不希望其值的每次更改都触发重新渲染,这将特别有用。

      但是,如果您需要更改值以触发组件中的重新渲染,那么useReactiveVar 就派上用场了。

      const TimerApolloState = () => {
        const count = useReactiveVar(countVar) <--------- declare in component 
        const logCountVar = () => {
          console.info(countVar())
        }
        
      
        React.useEffect(() => {
          const id = setInterval(() => {
            const newVal = countVar() + 1;
            countVar(newVal)
          }, 1000)
      
          return () => clearInterval(id)
        }, [])
      
        console.info("Render")
      
        return  (
        <>
          <h1>{count}</h1> <--------- this will now be up to date 
          <button onClick={logCountVar}>Log CountVar</button>
        </>)
      }
      

      最后,如果你查看useReactiveVar 的源代码——

      export function useReactiveVar(rv) {
          var value = rv();
          var setValue = useState(value)[1];
          useEffect(function () {
              var probablySameValue = rv();
              if (value !== probablySameValue) {
                  setValue(probablySameValue);
              }
              else {
                  return rv.onNextChange(setValue); <------ event listener updates local state value
              }
          }, [value]);
          return value; <------ return local state value, which will trigger re-render if the value changes
      }
      

      看起来他们使用类似于onchange 的事件发射器,只要存储变量的值发生变化。然后事件侦听器调用setValue 并返回value,这会触发重新渲染。

      我希望这有帮助!

    【讨论】:

      猜你喜欢
      • 2013-12-19
      • 2022-11-05
      • 2015-11-03
      • 1970-01-01
      • 1970-01-01
      • 2011-07-10
      • 2018-08-13
      • 1970-01-01
      • 2011-11-15
      相关资源
      最近更新 更多