在子组件重新渲染时丢失路由器对象

Posted

技术标签:

【中文标题】在子组件重新渲染时丢失路由器对象【英文标题】:Losing router object on child component rerender 【发布时间】:2020-08-09 08:22:35 【问题描述】:

我有一个父组件GoalList 映射到一个子组件:

            data.goals.map((item, index) => 
                return (
                    <Link
                        href= pathname: "/goal", query:  id: item.id  
                        key=`goal-item-$index`
                    >
                        <a>
                            <li>
                                <div>item.title</div>
                            </li>
                        </a>
                    </Link>
                );
            )

next/router的页面:

import SingleGoal from "../components/SingleGoal";

const Single = () => 
    return <SingleGoal />;
;

export default Single;

子组件:

const SingleGoal = () => 
    const [id, setId] = useState("");
    const router = useRouter();

    useEffect(() => 
        if (router.query.id !== "") setId(router.query.id);
    , [router]);

    const  loading, error, data  = useQuery(SINGLE_GOAL_QUERY, 
        variables:  id: id ,
    );

    if (loading) return <p>Loading...</p>;
    if (error) return `Error! $error.message`;

    return (
        <div>
            <h1>data.goal.title</h1>
            <p>data.goal.endDate</p>
        </div>
    );
;

当我点击父组件中的Link 时,item.id 被正确传输,SINGLE_GOAL_QUERY 正确执行。

但是,当我刷新 SingleGoal 组件时,路由器对象需要一瞬间来填充,并且我收到 GraphQL 警告:

[GraphQL error]: Message: Variable "$id" of required type "ID!" was not provided., Location: [object Object], Path: undefined

在一个类似的项目中,我之前给下一个/路由器的页面组件提供了道具,但这似乎不再起作用:

const Single = (props) => 
    return <SingleGoal id=props.query.id />;
;

如何解决路由器对象的延迟问题?这是使用 getInitialProps 的情况吗? 感谢您的任何指导。

【问题讨论】:

【参考方案1】:

在这种情况下,通过page 传输道具的秘诀是通过自定义_app 启用getInitialProps

之前:

const MyApp = ( Component, apollo, pageProps ) => 
    return (
        <ApolloProvider client=apollo>
            <Page>
                <Component ...pageProps />
            </Page>
        </ApolloProvider>
    );
;

之后:

const MyApp = ( Component, apollo, pageProps ) => 
    return (
        <ApolloProvider client=apollo>
            <Page>
                <Component ...pageProps />
            </Page>
        </ApolloProvider>
    );
;

MyApp.getInitialProps = async ( Component, ctx ) => 
    let pageProps = ;

    if (Component.getInitialProps) 
        // calls page's `getInitialProps` and fills `appProps.pageProps`
        pageProps = await Component.getInitialProps(ctx);
    
    // exposes the query to the user
    pageProps.query = ctx.query;
    return  pageProps ;
;

现在唯一的缺点是不再生成静态页面,每个请求都使用服务器端渲染。

【讨论】:

【参考方案2】:

您可以通过重新排序挂钩来使用路由器查询 id 设置组件内部的初始状态

const SingleGoal = () => 
    const router = useRouter();
    const [id, setId] = useState(router.query.id);

    useEffect(() => 
        if (router.query.id !== "") setId(router.query.id);
    , [router]);

    const  loading, error, data  = useQuery(SINGLE_GOAL_QUERY, 
        variables:  id: id ,
    );

    if (loading) return <p>Loading...</p>;
    if (error) return `Error! $error.message`;

    return (
        <div>
            <h1>data.goal.title</h1>
            <p>data.goal.endDate</p>
        </div>
    );
;

【讨论】:

这似乎并没有阻止 useQuery 在 id 仍未定义未定义时执行。我收到关于未提供 ID 的相同控制台警告

以上是关于在子组件重新渲染时丢失路由器对象的主要内容,如果未能解决你的问题,请参考以下文章

React:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用

[react-router] React-Router 4怎样在路由变化时重新渲染同一个组件?

React 和 antd:路由器不会重新渲染组件

使用 React Router 重新加载 Webpack HotModule 渲染与组件

当多个路由地址对应同一组件时,切换路由组件不刷新的问题

React-Router 在路由更改时重新安装组件