Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using with dynamic import such as React.Lazy or react-loadable #25

Closed
iam-frankqiu opened this issue Jul 31, 2019 · 27 comments
Closed

Using with dynamic import such as React.Lazy or react-loadable #25

iam-frankqiu opened this issue Jul 31, 2019 · 27 comments
Labels
bug Something isn't working resolved the issue has been resolved

Comments

@iam-frankqiu
Copy link

https://codesandbox.io/s/react-live-route-demo-1-ozsw4
This is the demo. open the console and try to click more.it can't reappear every time.

@fi3ework
Copy link
Owner

fi3ework commented Jul 31, 2019

When using with dynamic import. You should invoke ensureDidMount manually as react-live-route can't get the target DOM when the DOM mounting. You can add this line here and it should works fine.
image

@fi3ework fi3ework pinned this issue Jul 31, 2019
@fi3ework fi3ework changed the title There are some errors happened in dynamic imports component in your LiveRoute Using with dynamic import such as React.Lazy or react-loadable Jul 31, 2019
@fi3ework
Copy link
Owner

It's also mentioned in document. See here :)

@iam-frankqiu
Copy link
Author

iam-frankqiu commented Jul 31, 2019

It's also mentioned in document. See here :)

Is this method built-in to React or I should write it by myself? Because if I using React. lazy it may not have this method in React, how should I do?

@fi3ework
Copy link
Owner

fi3ework commented Jul 31, 2019

@QuanhuQiu ensureDidMount is provided by react-live-route. You should manually invoke it in componentDidMount.

@iam-frankqiu
Copy link
Author

@QuanhuQiu ensureDidMount is provided by react-live-route. You should manually invoke it in componentDidMount.

I have invoked it in List and About component, but it seems like doesn't work. The following message was shown in the console:
The above error occurred in the component:
in LiveRoute (created by Route)

in Route (created by withRouter(LiveRoute))

in withRouter(LiveRoute) (created by App)

in Suspense (created by App)

in div (created by App)

in App

in Router (created by BrowserRouter)

in BrowserRouter

Consider adding an error boundary to your tree to customize error handling behavior.

Visit https://fb.me/react-error-boundaries to learn more about error .
https://codesandbox.io/s/react-live-route-demo-1-ozsw4
Above is the demo. waiting online, it's urgent. Thanks a lot

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu Write index.jsx like this: https://codesandbox.io/s/react-live-route-demo-1-ow3d1
I'm not so familiar with suspense and lazy, but there might be some pitfalls using with react-router.

import React, {lazy, Suspense} from "react";
import ReactDOM from "react-dom";
import { Route, BrowserRouter, withRouter, Switch } from "react-router-dom";
import NotLiveRoute from "react-live-route";
// import List from "./list";
import Detail from "./detail";
import Bar from "./bar";
// import About from "./about";
import Home from "./home";
import NotFound from "./notFound";
import "./styles.css";

const WaitingList = props => (
    <Suspense fallback={<div>Loading...</div>}>
      <List {...props} />
    </Suspense>
  );

  const WaitingAbout = props => (
    <Suspense fallback={<div>Loading...</div>}>
      <About {...props} />
    </Suspense>
  );


const List = lazy(() => (import('./list')))

const About = lazy(() => (import('./about')))

const LiveRoute = withRouter(NotLiveRoute);

function App() {
  return (
    <div className="App">

    <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/item/:id" component={Detail} />
        <Route path="/items" />
        <Route path="/about" />
        <Route path="*" render={NotFound} />
      </Switch>
      <LiveRoute
        path="/items"
        component={WaitingList}
        livePath={"/item/:id"}
        name="items"
        onHide={(location, match, history, livePath, alwaysLive) => {
          console.log("hide hook tiggered");
        }}
        onReappear={(location, match, history, livePath, alwaysLive) => {
          console.log("reappear hook tiggered");
        }}
      />
      <LiveRoute
        path="/about"
        alwaysLive={true}
        component={WaitingAbout}
        name="about"
      />
      <Bar />
     
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  rootElement
);

if ("scrollRestoration" in window.history) {
  // 默认值为 'auto'
  // window.history.scrollRestoration = 'manual'
}

document.addEventListener("scrollTo", () => {
  console.log("document scrolled");
});

@iam-frankqiu
Copy link
Author

iam-frankqiu commented Aug 1, 2019

@QuanhuQiu Write index.jsx like this: https://codesandbox.io/s/react-live-route-demo-1-ow3d1
I'm not so familiar with suspense and lazy, but there might be some pitfalls using with react-router.

import React, {lazy, Suspense} from "react";
import ReactDOM from "react-dom";
import { Route, BrowserRouter, withRouter, Switch } from "react-router-dom";
import NotLiveRoute from "react-live-route";
// import List from "./list";
import Detail from "./detail";
import Bar from "./bar";
// import About from "./about";
import Home from "./home";
import NotFound from "./notFound";
import "./styles.css";

const WaitingList = props => (
    <Suspense fallback={<div>Loading...</div>}>
      <List {...props} />
    </Suspense>
  );

  const WaitingAbout = props => (
    <Suspense fallback={<div>Loading...</div>}>
      <About {...props} />
    </Suspense>
  );


const List = lazy(() => (import('./list')))

const About = lazy(() => (import('./about')))

const LiveRoute = withRouter(NotLiveRoute);

function App() {
  return (
    <div className="App">

    <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/item/:id" component={Detail} />
        <Route path="/items" />
        <Route path="/about" />
        <Route path="*" render={NotFound} />
      </Switch>
      <LiveRoute
        path="/items"
        component={WaitingList}
        livePath={"/item/:id"}
        name="items"
        onHide={(location, match, history, livePath, alwaysLive) => {
          console.log("hide hook tiggered");
        }}
        onReappear={(location, match, history, livePath, alwaysLive) => {
          console.log("reappear hook tiggered");
        }}
      />
      <LiveRoute
        path="/about"
        alwaysLive={true}
        component={WaitingAbout}
        name="about"
      />
      <Bar />
     
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  rootElement
);

if ("scrollRestoration" in window.history) {
  // 默认值为 'auto'
  // window.history.scrollRestoration = 'manual'
}

document.addEventListener("scrollTo", () => {
  console.log("document scrolled");
});

比较急 我就不用英文了,你那个组件我之前一直用的好好的,但是这次我做用React.lazy Suspense
做了一次 code spiltting 就出问题了,而且还是无法复现的,但是频率挺高的,你这个demo这样写在你这个比较简单的业务场景下是没有那个问题了,但是我这是生产环境啊大哥,里面逻辑非常复杂的好吧,后来我换成用react-loadable做code-splitting,发现还是会报那个错误,反正只要用了你这个组件做code splitting就会报那个错误,我这生产环境非常急啊,希望你能理解下,因为我之前想着用新的库去实现这个功能,发现别的库也不好用,我当初选择用这个库也是因为你的头像很可爱啊,赶快解决下这个问题,发个新的版本。因为我真的没有办法把我这个问题直接搬到sandbox里面去复现,因为业务代码太复杂, 根本就不知道是哪里的逻辑导致的。希望能理解
企业微信截图_5df42cf9-b43c-4b11-b9c4-e0d869edce76

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

  1. @QuanhuQiu 你这样讲你这么复杂我也不知道你是怎么复杂的啊... 如果是因为 Suspense 和 lazy 的问题就像我上面那个 demo 里写的一样就好了吧,给每个 lazy 的组件套一个 Suspense 来解决,坦率的讲我没用过 Suspense,上面的 demo 是参考的 https://www.peterbe.com/plog/react-16.6-with-suspense-and-lazy-loading-components-with-react-router-dom 这个文章。
  2. 如果按上面改了还是不行是为什么不行呢?
  3. 粗暴一点的方法是设计到 LiveRoute 的页面不要用 Suspense,我理解需要 LiveRoute 的场景应该不是非常多的。
  4. 炮姐当然可爱啦 :)

@iam-frankqiu
Copy link
Author

It's also mentioned in document. See here :)

  1. @QuanhuQiu 你这样讲你这么复杂我也不知道你是怎么复杂的啊... 如果是因为 Suspense 和 lazy 的问题就像我上面那个 demo 里写的一样就好了吧,给每个 lazy 的组件套一个 Suspense 来解决,坦率的讲我没用过 Suspense,上面的 demo 是参考的 https://www.peterbe.com/plog/react-16.6-with-suspense-and-lazy-loading-components-with-react-router-dom 这个文章。
  2. 如果按上面改了还是不行是为什么不行呢?
  3. 粗暴一点的方法是设计到 LiveRoute 的页面不要用 Suspense,我理解需要 LiveRoute 的场景应该不是非常多的。
  4. 炮姐当然可爱啦 :)
    算了 那我自己来debug一下 给你提个pr 行吧 不要辜负炮姐啊

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu 我已经说了解决方法了啊,是「业务太复杂不能采用这种解决方法」吗 😅

@iam-frankqiu
Copy link
Author

@QuanhuQiu 我已经说了解决方法了啊,是「业务太复杂不能采用这种解决方法」吗 😅

我给你提个 pr 行吧!以后万一这个库火了,别忘了我哈 ps:其实我更喜欢黑子

@iam-frankqiu
Copy link
Author

@QuanhuQiu 我已经说了解决方法了啊,是「业务太复杂不能采用这种解决方法」吗 😅

业务比较复杂 用了那种方法还是出bug了啊 ,你们现在的项目没做 code splitting吗?你们用的也是react-loadable? 你们下次可以试试在你们的项目里面使用Suspense, React.lazy看看有没有问题啊?

@iam-frankqiu
Copy link
Author

@QuanhuQiu 我已经说了解决方法了啊,是「业务太复杂不能采用这种解决方法」吗 😅

I have already fixed the bug about React.lazy. you only need to merge my PR and republished again. Thanks a lot. Forgive my poor English...

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu Thanks. I notice that you change import * as ReactDOM from 'react-dom' to import {findDOMNode} from 'react-dom'. It's the import mode cause the error you met? Or the removed this.getRouteDom() cause the error?

@iam-frankqiu
Copy link
Author

@QuanhuQiu Thanks. I notice that you change import * as ReactDOM from 'react-dom' to import {findDOMNode} from 'react-dom'. It's the import mode cause the error you met? Or the removed this.getRouteDom() cause the error?
还是讲中文吧!上面那个findDOMNode 是因为这样引进可以更好地去做 tree-shaking. 下面这个this.getRouteDom()我也不知道为啥把这行代码去掉我那里就没有那个bug了,今天为了这个bug都折腾一天了 ,react react-dom源代码都看了些,再折腾下去react源代码都快看完了。后来我就直接把那里的getRouteDom()去掉了然后bug就好了,然后跑了一遍测试用例也都是通过的,所以我觉得应该没什么问题。当然具体的原因我也回答不了,反正这个估计要去看react 里面的内部实现了。之前看了你两篇blog,还挺喜欢的,还是支持一下你 ,支持一下这个库。重新发个版这个issue就可以关掉了。

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu 由于我不能复现这个 bug,可以先尝试将 getRouteDom 这个函数改成下面这样,然后不要把 componentDidUpdate 里的 getRouteDom 删掉

public getRouteDom = () => {
  let routeDom
  try {
    routeDom = ReactDOM.findDOMNode(this)
  } catch{}
  this.routeDom = (routeDom as CacheDom) || this.routeDom	   
}

试一下,这个 error 是 react throw 出来的,会破坏整个组件的渲染,感谢

@iam-frankqiu
Copy link
Author

@QuanhuQiu 由于我不能复现这个 bug,可以先尝试将 getRouteDom 这个函数改成

public getRouteDom = () => {
  let routeDom
  try {
    routeDom = ReactDOM.findDOMNode(this)
  } catch{}
  this.routeDom = (routeDom as CacheDom) || this.routeDom	   
}

试一下,这个 error 是 react throw 出来的,会破坏整个组件的渲染,感谢
这样好像也可以 那改成这样也行吧,但是 我另外一个组件也是用了这个findDOMNode这个方法,但是只是在componentDidMount这个生命周期里面用了,其它地方没用,也没有try catch 但是也没有这些问题,所以你要是担心的话直接try catch一下也行吧,还有这个其实还有些别的优化的地方,首页这个findDOMNode这个方法不能用于函数式组件,还有就是直接display: none是不是太简单粗暴了,那些动画好像就会没有,不过总得来说,用ts写的,感觉比其他的一些要靠谱多了。

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu 在 componentDidUpdate 中调用 getRouteDOM 确实是比较多余的一个操作,好像当初是为了应对异步加载这样做的(虽然没起到作用还引入了坑)。

@iam-frankqiu
Copy link
Author

@QuanhuQiu 在 componentDidUpdate 中调用 getRouteDOM 确实是比较多余的一个操作,好像当初是为了应对异步加载这样做的(虽然没起到作用还引入了坑)。
所以我觉得加上try catch 然后再在componentDidUpdate 里面去掉更好一些吧,赶快发个新版吧。等你的版本啊

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu 已更,加了 try catch,cdu 中还是要加 getRouteDOM 的

@iam-frankqiu
Copy link
Author

@QuanhuQiu 已更,加了 try catch,cdu 中还是要加 getRouteDOM 的
https://github.com/loktar00/react-lazy-load/blob/master/src/LazyLoad.jsx
你看这个也是用了类似的方法,就每一在cdu里面加

@fi3ework
Copy link
Owner

fi3ework commented Aug 1, 2019

@QuanhuQiu 赞!

@fi3ework fi3ework added bug Something isn't working resolved the issue has been resolved labels Aug 1, 2019
@hzfvictory
Copy link

我用React.lazy Suspense做code-splitting,也遇到无法正常缓存路由,偶尔把上一个的路由里面的dom,都带过来了:下面是我的代码


//在这用的路由缓存
const keepLive = (item, index) => {
    return (
        <LiveRoute
            key={index}
            path={item.path}
            render={props => {
                const {staticContext, ...options} = props;
                //routes={item.routes}
                document.title = item.name || "展示DEMO";
                return (item && <item.component {...options} />)
            }}
            livePath={"/base/order"}
            name="items"
            onHide={(location, match, history, livePath, alwaysLive) => {
                console.log("hide hook tiggered");
            }}
            onReappear={(location, match, history, livePath, alwaysLive) => {
                console.log("reappear hook tiggered");
            }}
        />
    )
};


// 循环渲染当前路由数组中一维数组中的组件
export const RenderRoutes = ({routes}, type) => {
    if (type === 'keep') {
        return (
            routes.map((route, i) => keepLive(route, i))
        )
    }
    return (
        <Suspense fallback={<Initialize/>}>
            <Switch>
                {routes.map((route, i) => RouteWithSubRoutes(route, i))}
                {/*TODO: 优化在此添加所有的重定向,Redirect必须加到最后面*/}
                <Redirect from={'/'} exact to={'/base/order'}/>;
                <Redirect from={'/base'} exact to={'/base'}/>;
                <Redirect from={'/*'} exact to={'/error'}/>;
            </Switch>
        </Suspense>
    )
};

@fi3ework
Copy link
Owner

fi3ework commented Sep 20, 2019

@hzfvictory 使用 ensureDidMount,可以先参考下文档。

@hzfvictory
Copy link

@hzfvictory使用ensureDidMount,可以先参考下文档。

有用到 ensureDidMount ,在componentDidMount,不过还是那个问题

@fi3ework
Copy link
Owner

@hzfvictory
配合 Switch 有一点特殊,可以参考一下 #25 (comment)

@hzfvictory
Copy link

@hzfvictory
配合 Switch 有一点特殊,可以参考一下 #25 (comment)

哦哦,放到switch外面,一会我试试

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working resolved the issue has been resolved
Projects
None yet
Development

No branches or pull requests

3 participants