【问题标题】:Component not rendering when using children as function将子级用作函数时组件不呈现
【发布时间】:2020-06-08 12:40:18
【问题描述】:

我正在使用react-apollo 查询graphQL 服务器,并能够成功地为客户端提供数据。由于会有多个地方查询数据,因此我将尝试创建一个容器(重构)来封装useQuery 挂钩,以便可以在一个地方使用它。

第一次尝试(按预期工作)

const HomeContainer = () => {
  const { data, error, loading } = useQuery(GET_DATA_QUERY, {
    variables: DATA_VARIABLES
  });
  const [transformedData, setTransformedData] = useState();

  useEffect(() => {
    if(!!data) {
      const transformedData = someTransformationFunc(data);

      setTransformedData(...{transformedData});
    }
  }, [data]);

  if (loading) {
    return <div>Loading data ...</div>;
  }

  if (error) {
    return <p>Error loading data</p>;
  }
  if (!data) {
    return <p>Not found</p>;
  }

  return <Home transformedData={transformedData} />;
};

我想将围绕查询的不同阶段的仪式封装到一个新容器中(加载、错误状态),这样我就可以减少代码重复。

第一次尝试重构

  • 查询容器在queryvariablescallback 中传递。这负责根据查询的状态(加载、错误或没有数据返回时)返回不同的节点。

const HomeContainer = () => {
  const {data, error, loading} = useQuery(GET_DATA_QUERY, {
    variables: DATA_VARIABLES
  });
  const [transformedData, setTransformedData] = useState();

  const callback = (data) => {
    const transformedData = someTransformationFunc(data);

    setTransformedData(...{
      transformedData
    });
  };

  return ( 
     <QueryContainer 
        query={GET_DATA_QUERY}
        variables={DATA_VARIABLES}
        callback ={callback} 
     >
        <Home transformedData={transformedData} />
     </QueryContainer>
  )
};

const QueryContainer = ({callback, query, variables, children }) => {
  const {data, error, loading } = useQuery(query, {
    variables: variables
  });

  // Once data is updated invoke the callback
  // The transformation of the raw data is handled by the child
  useEffect(() => {
    if (!!data) {
      callback(data);
    }
  }, [data]);

  if (loading) {
    return <div > Loading data... < /div>;
  }

  if (error) {
    return <p > Error loading data < /p>;
  }
  if (!data) {
    return <p > Not found < /p>;
  }

  return children;
};

QueryContainer 正在使用useEffect 并在data 返回时调用callback。我觉得这有点混乱,违背了封装在父级中并使用callback 与子级交谈和更新的目的。

第三次尝试(使用子函数)

摆脱回调并将数据作为第一个参数传递给children 函数。

const HomeContainer = () => {
    return (
        <QueryContainer
            query={GET_DATA_QUERY}
            variables={DATA_VARIABLES}
        >   
           {(data) => {
               const transformedData = someTransformationFunc(data);

                return <Home transformedData={transformedData} />;
           }}

        </QueryContainer>
    )
};

const QueryContainer = ({ query, variables, children }) => {
    const { data, error, loading } = useQuery(query, {
        variables: variables
    });

    if (loading) {
        return <div>Loading data ...</div>;
    }

    if (error) {
        return <p>Error loading data</p>;
    }
    if (!data) {
        return <p>Not found</p>;
    }

    return children(data);
};

我希望这会起作用,因为没有真正改变,并且当 data 更新时,新的渲染会以 data 作为参数调用子函数。 但是当我导航到那条路线时,我看到一个黑屏(没有错误,我可以看到登录到控制台的正确数据) 如果我再次单击该链接,我可以看到提交给 DOM 的组件。

不太确定这里发生了什么,想知道是否有人可以阐明这里发生了什么。

【问题讨论】:

    标签: reactjs graphql react-apollo react-apollo-hooks


    【解决方案1】:

    我在上面添加的代码 sn-ps 在隔离时按预期工作。

    https://codesandbox.io/s/weathered-currying-4ohh3

    问题出在层次结构树下的某个其他组件导致组件无法重新渲染。

    第二个实现按预期工作,因为组件由于从父级调用的回调而再次呈现。

    【讨论】:

      【解决方案2】:

      嗯,应该可以了……

      试试这样的(组件注入,有点像 HOC - inspiration):

      const HomeContainer = () => {
        return (
          <QueryContainer
              query={GET_DATA_QUERY}
              variables={DATA_VARIABLES}
              transformation={someTransformationFunc}
              renderedComponent={Home}
          />   
        )
      };
      
      const QueryContainer = ({ query, variables, transformation, renderedComponent: View }) => {
        const { data, error, loading } = useQuery(query, { variables });
      
        if (loading) {
          return <div>Loading data ...</div>;
        }
      
        if (error) {
          return <p>Error loading data</p>;
        }
        if (!data) {
          return <p>Not found</p>;
        }
      
        // let transformedData = transformation(data);
        // return <View transformedData={transformedData} />;
      
        return <View transformedData={transformation ? transformation(data) : data} />;
      };
      

      如果仍然无法正常工作 (!?),请将 datatransformation 作为道具传递并使用它们来初始化状态(使用 useStateuseEffect)。

      你真的/仍然需要&lt;HomeContainer/&gt; 作为抽象吗? ;)

      【讨论】:

      • Do you really/still need &lt;HomeContainer/&gt; as an abstraction? 不是真的,我只是想提出一个简约的例子。俱乐部 useEffectuseState 肯定会起作用(第二个示例使用它)。我试图了解可能会发生什么,因为当使用 child 作为函数时它没有被正确渲染
      • 我的意思是 &lt;Home/&gt; const {someState, setSomeState}= useState(transformation(data)); 内的 1-liner ...我的解决方案有效吗?你需要知道为什么你不这样做?
      • 我没有检查您的解决方案是否有效。但从技术上讲,impl与我在问题中的第三个非常相似,children as a func vs HOC
      • 当然,工作与不工作之间的差异可能很小;)
      • 发现问题。实施按预期工作。不幸的是,问题出在其他一些有条件地渲染容器的组件上,因此这似乎不起作用。它适用于前 2 个实现,因为 setState 被调用。这是工作示例 -codesandbox.io/s/weathered-currying-4ohh3
      猜你喜欢
      • 2022-11-01
      • 1970-01-01
      • 2022-06-13
      • 2018-08-16
      • 2018-01-08
      • 1970-01-01
      • 2017-12-17
      • 1970-01-01
      • 2019-07-28
      相关资源
      最近更新 更多