2

我正在使用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} />;
};

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

第一次尝试重构

  • Query 容器在query,variablescallback. 这负责根据查询的状态(加载、错误或没有数据返回时)返回不同的节点。

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并调用callback何时data返回。我觉得这有点混乱,违背了封装在父母中并使用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 的组件。

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

4

2 回答 2

0

我在上面添加的代码片段独立地按预期工作。

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

问题在于层次结构树中的其他一些组件导致组件不重新渲染。

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

于 2020-02-25T21:26:52.017 回答
0

嗯,应该工作......

尝试这样的事情(组件注入,有点像 HOC -灵感):

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作为道具传递并使用它们来初始化状态(使用useStateor useEffect)。

你真的/仍然需要<HomeContainer/>抽象吗?;)

于 2020-02-25T00:21:51.890 回答