Coder Social home page Coder Social logo

stratiformltd / react-loadable-visibility Goto Github PK

View Code? Open in Web Editor NEW
1.0K 7.0 49.0 523 KB

A wrapper around react-loadable and @loadable/component to load elements once they are visible on the page.

License: BSD 3-Clause "New" or "Revised" License

JavaScript 100.00%

react-loadable-visibility's Introduction

react-loadable-visibility

A wrapper around react-loadable and @loadable/component, only loading imports that are visible on the page.

npm version Build Status

Example using react-loadable

import LoadableVisibility from "react-loadable-visibility/react-loadable";
import Loading from "./my-loading-component";

const LoadableComponent = LoadableVisibility({
  loader: () => import("./my-component"),
  loading: Loading
});

export default function App() {
  return <LoadableComponent />;
}

Example using @loadable/component

import loadableVisibility from "react-loadable-visibility/loadable-components";
import Loading from "./my-loading-component";

const LoadableComponent = loadableVisibility(() => import("./my-component"), {
  fallback: <Loading />
});

export default function App() {
  return <LoadableComponent />;
}

Options

The API is exactly the same as the original library. Please refer to their documentation:

Note that you'll need to have react-loadable or @loadable/component in your package.json.

How does this work?

It's in essence a wrapper around loadable libraries with hooks into an IntersectionObserver to inform us of when a given element is in the viewport.

Therefore, it will only function in browsers that have the IntersectionObserver API.

A polyfill for IntersectionObserver is available however I am skeptical of its performance but have not tested it fully to offer a recommendation here. If you have any comments about this, feel free to open a PR and adjust this README!

If you choose the use the polyfill, you can load it via a polyfill.io script - <script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver%2CIntersectionObserverEntry"></script>

Otherwise if the IntersectionObserver API is not available, we will revert back to just using react-loadable or @loadable/component itself.

Why do I want this?

react-loadable and @loadable/component are fantastic higher level components to load additional modules once they are mounted on your page. It's great for keeping your bundle size small and pulling in a larger payload when the required components are part of your tree.

However it will not account for the content that's currently visible on your page, and only load what's actually visible to the end user. If you have a long page and are loading the entire content of that page for the user, even though they may only be able to see the content above the fold, it can be wasteful and especially detrimental in a mobile context.

react-loadable-visibility is positioned to solve these issues by leveraging the existing awesome API of loadable libraries with an extension to only trigger the loading of additional content once that component comes into view.

Contributors

We'd like to thank the following people for their contributions:

Have a look at the GitHub contributors page for a full list of all contributors.

License

react-loadable-visibility may be redistributed according to the BSD 3-Clause License.

Copyright 2019, Stratiform Limited.

react-loadable-visibility's People

Contributors

dependabot[bot] avatar dijonkitchen avatar gregberge avatar n1313 avatar robinsiep avatar s524797336 avatar sauravvarma avatar tazsingh avatar the83 avatar zubrolet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-loadable-visibility's Issues

Visibility container restricts LoadingComponent size/layout

Since the LoadingComponent is wrapped in a div with an inline-block style, it can't span the entire width of the grandparent (parent being the div). This is solvable by letting the user set the container style themselves for their needs. Sometimes leaving the div's default block is good enough.

You can see this change here:

https://github.com/stratiformltd/react-loadable-visibility/compare/master...jahed:feature/containerStyle?expand=1

I didn't send a pull request as it is a breaking change and possibly unwanted. I didn't default containerStyle to inline-block for backwards compatibility as it seemed like an odd default for my usages.

Of course, this doesn't solve the problem where say you have a grandparent which using flex which prevents the LoadingComponent from working off it (since it's not a direct descendant, unlike LoadableComponent). Since we need a container ref to check visibility, the only other option I can see is to ask the user to pass the ref prop down through their LoadingComponent all the way to wherever the DOM components are rendered.

Just thought I'd bring all of this up, to see if it's considered an issue.

no declaration file

Just got this error on Visual Studio Code when attempting to use react-loadable-visibility.

I am using npm v 6.4.1 and latest react/webpack as well.

Also I was already using react-loadable separately without that issue.

 Could not find a declaration file for module 'react-loadable-visibility/react-loadable'. '/.../.../Sites/app/node_modules/react-loadable-visibility/react-loadable.js' implicitly has an 'any' type.

Thanks

The app component:

import React from 'react';
import { Provider } from 'react-redux';
import {
    Route,
    BrowserRouter as Router,
    Switch
} from 'react-router-dom';
import PropTypes from 'prop-types';
import Loadable from 'react-loadable';
import LoadableVisibility from 'react-loadable-visibility/react-loadable';
import PrivateRoute from './auth/auth';
import Header from './pages/common/header/';
import { Loading } from './tools/functions';


const HomePage = Loadable({
    loader: () => import('./pages/home/'),
    loading: Loading,
    delay: 100
});

 .......

const Footer = LoadableVisibility({
    loader: () => import('./pages/common/footer/'),
    loading: Loading,
  })

Additionally, I do have an issue with react-loadable not retrieving files from cache when used with service-worker. I could not find where to report on the react-loadable repository. See this question on SO, https://stackoverflow.com/questions/52812620/react-loadable-does-not-retrieve-from-cache

Not working with loadable SSR

I'm using it with:

  • Gatsby;
  • gatsby-plugin-loadable-components-ssr; and
  • @loadable/component

when I run gatsby build it crash with this error message:

failed Building static HTML for pages - 1.019s

 ERROR #95313 

Building static HTML failed for path "/"

See our docs page for more info on this error: https://gatsby.dev/debug-html


   10 | function invariant(condition, message) {
   11 |   if (condition) return;
 > 12 |   var error = new Error("loadable: " + message);
      | ^
   13 |   error.framesToPop = 1;
   14 |   error.name = 'Invariant Violation';
   15 |   throw error;


  WebpackError: Invariant Violation: loadable: SSR requires `@loadable/babel-plugin`, please install it
  
  - loadable.esm.js:12 
    node_modules/@loadable/component/dist/loadable.esm.js:12:1
  
  - loadable.esm.js:153 
    node_modules/@loadable/component/dist/loadable.esm.js:153:1

I then installed the @loadable/babel-plugin package but I had no luck! The error continues to trigger!
There's no error if I remove the react-loadable-visibility call from the code.

Any ideas?

Not working when component itself has more content to cover visible area

i have 2 components
ComponentA & ComponentB

i have added h1 tag in ComponentA and img tag ComponentB
both components are included in one main component called App Component
both components have console.log() added
Now

  1. when i add lots of <br /> between both component in App component it first log ComponentA and when i scroll to bottom when ComponentB is visible it logs it in console. which is as expected and works fine
  2. now when i remove those lots of <br /> added between both component and add that
    in ComponentA, it logs both ComponentA & ComponentB in console even when ComponentB is not visible in screen yet.

find attached code with issue , check log in chrome console. https://www.screencast.com/t/7kW0kxyLH3

react-loadable-visibility.zip

Remove props from placeholder div

Hi!

I'm passing props to my LoadableComponent elements, but those props are also passed to the placeholder div elements so I'm getting some annoying warnings:

Warning: React does not recognize the `myCustomProp` prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom attribute, 
spell it as lowercase `myCustomProp` instead. If you accidentally passed it
from a parent component, remove it from the DOM element.

Should be easy enough to remove these warnings by deleting lines 80 and 96 in createLoadableVisibilityComponent.js, but the better way would probably be to extract the className prop and continue to apply it to the placeholder div (i.e., {...props} -> className={props.className}) in case anyone is relying on that functionality (also in your tests, I believe).

Larger code block for context:

    if (LoadingComponent || props.fallback) {
      return (
        <div
          style={{
            display: "inline-block",
            minHeight: "1px",
            minWidth: "1px"
          }}
          className={props.className}  #CHANGE: previously {...props}
          ref={visibilityElementRef}
        >
          {LoadingComponent
            ? React.createElement(LoadingComponent, {
                isLoading: true,
                ...props
              })
            : props.fallback}
        </div>
      );
    }

    return (
      <div
        style={{ display: "inline-block", minHeight: "1px", minWidth: "1px" }}
        className={props.className}  #CHANGE: previously {...props}
        ref={visibilityElementRef}
      />
    );
  }

Is there another reason for applying the props to the placeholder div elements? Happy to create a PR if you're in agreement.

Lazy loading images?

Hi @tazsingh, I came across this component, when looking to lazy load images. While I see its utility in general to not load components which are not visible - I am wondering if you'd recommend using this for lazy loading images? Or would this be an overkill/underkill?

Thank you for putting out this library!

no declaration file

Getting the error below on Visual Studio Code when attempting to use react-loadable-visibility.

I was already using react-loadable without this issue.

 Could not find a declaration file for module 'react-loadable-visibility/react-loadable'. '/Users/BAMAC/Sites/pwa3/node_modules/react-loadable-visibility/react-loadable.js' implicitly has an 'any' type.

I am using npm (6.4.1), react(16.5.2) and webpack(3.8.1) versions.

For whatever it's worth, here's some excerpts of app.js code:

 ......
 import Loadable from 'react-loadable';
import LoadableVisibility from 'react-loadable-visibility/react-loadable';
import PrivateRoute from './auth/auth';
import Header from './pages/common/header/';
import { Loading } from './tools/functions';


const HomePage = Loadable({
    loader: () => import('./pages/home/'),
    loading: Loading,
    delay: 100
});

const Footer = LoadableVisibility({
    loader: () => import('./pages/common/footer/'),
    loading: Loading,
  })

Additionally, I do have an issue with react-loadable not retrieving files from cache when used with service-worker. I could not find where to report on react-loadable's repository.
I did try SO, without success.

update to @loadable/components package

Do you want to request a feature or report a bug?
feature / enhancement

What is the current behaviour?

loadable-components package is used

Desired behaviour

should use @loadable/component

As loadable-components are now @loadable/component and the old one is deprecated, please update to the new dependency, as your import breaks with the new version (name change).

Enable IntersectionObserver options

Do you want to request a feature or report a bug?
feature

What is the current behaviour?

Uses default intersection options.

Desired behaviour

It would be a nice feature to be able to send down IntersectionObserver options, in particular the rootMargin option. This would allow for better UX when loading components lazily.

I would be happy to contribute to this feature if this is something you would like to add to the library.

warning occur when use react-loadable-visibility,but react-loadable is ok

warning occur when use react-loadable-visibility,but using react-loadable is ok

package.json

    "react-loadable": "^5.5.0",
    "react-loadable-visibility": "^3.0.2",
    "react-redux": "^7.2.0",
    "react-router-dom": "^5.1.2",

using react-loadable is ok

import React from 'react';
import LoadableVisibility from 'react-loadable';

//通用的过场组件
const loadingComponent =()=>{
    return (
        <div>loading</div>
    )
}

//过场组件默认采用通用的,若传入了loading,则采用传入的过场组件
export default (loader,loading = loadingComponent)=>{
    return Loadable({
        loader,
        loading
    });
}

but react-loadable-visibility/react-loadable,warning happen

import React from 'react';
import LoadableVisibility from 'react-loadable-visibility/react-loadable'; //1

//通用的过场组件
const loadingComponent =()=>{
    return (
        <div>loading</div>
    )
}

//过场组件默认采用通用的,若传入了loading,则采用传入的过场组件
export default (loader,loading = loadingComponent)=>{
    return LoadableVisibility({ //2
        loader,
        loading
    });
}

image

LoadableVisibilityComponent doesn't expose loadable's load function.

I am using react-loadable-visibility to achieve lazy imports for a lot of my components and I was trying to synchronously (on a button click) , scroll the user to bottom but the components were not yet loaded so to achieve that, I was trying to benefit from loadable's load function

import loadable from "react-loadable-visibility/loadable-components"

const VideoPlayer = loadable(() => import("@components/VideoPlayer"), { ssr: false, fallback: <div></div>, })

The loadable function here returns LoadableVisibilityComponent which exposes preload method but not the load method.

const onButtonClick = () => { VideoPlayer.load().then(() => { // scroll element to view after component bundle is fetched }) }

During runtime, after button click it throws ->

index.js:1924 Uncaught TypeError: VideoPlayer.load is not a function
at eval (index.js:1924)
at Array.forEach ()
at scrollToAccordion (index.js:1923)
at redirect (index.js:2174)
at handleClick (FactBox.js:143)
at onClick (FactBox.js:214)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackDev (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:307)
eval @ index.js:1924
scrollToAccordion @ index.js:1923
redirect @ index.js:2174
handleClick @ FactBox.js:143
onClick @ FactBox.js:214
callCallback @ react-dom.development.js:189
invokeGuardedCallbackDev @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:307
executeDispatch @ react-dom.development.js:390
executeDispatchesInOrder @ react-dom.development.js:415
executeDispatchesAndRelease @ react-dom.development.js:3279
executeDispatchesAndReleaseTopLevel @ react-dom.development.js:3288
forEachAccumulated @ react-dom.development.js:3260
runEventsInBatch @ react-dom.development.js:3305
runExtractedPluginEventsInBatch @ react-dom.development.js:3515
handleTopLevel @ react-dom.development.js:3559
batchedEventUpdates$1 @ react-dom.development.js:21903
batchedEventUpdates @ react-dom.development.js:1061
dispatchEventForLegacyPluginEventSystem @ react-dom.development.js:3569
attemptToDispatchEvent @ react-dom.development.js:4268
dispatchEvent @ react-dom.development.js:4190
unstable_runWithPriority @ scheduler.development.js:653
runWithPriority$1 @ react-dom.development.js:11062
discreteUpdates$1 @ react-dom.development.js:21919
discreteUpdates @ react-dom.development.js:1072
dispatchDiscreteEvent @ react-dom.development.js:4169

Solution -> Expose loadable-component's load method as well

not working - file is fetched on page load

This may be related to this issue.

To answer to what was asked on above and to clarify the usage: I am building a PWA. Of course the intention is to be able, as much as possible to work offline.

So, it's great that react-loadable and visibility only fetch the resources when needed.

It would be even better that the fetching happens from the cache built by the service worker or just from the cache, full stop. It does not matter how the cache came about.

Neither is working in the case of react-loadable-visibility. Files are being built separately, but fetching occurs on page load. This is specifically the case of the Footer component on app below.

For react-loadable the first part ( fetched when needed does work ).

I tested on Firefox and Chrome.

Concerning fetching from cache which is, of course, a separate issue, I just mean to respond to the comment on issue above:

it would be impossible for this library to influence caching as there is nothing related to caching or service workers in it.

Somehow the browser is being told to ignore cache, which is perfectly possible. May be its not an issue with the plugin, but somewhere there's an issue.

Here's some code:

App.js

  import React from 'react';
  ....
 import Loadable from 'react-loadable';
 import LoadableVisibility from 'react-loadable-visibility/react-loadable';
 import PrivateRoute from './auth/auth';
 import Header from './pages/common/header/';
 import { Loading } from './tools/functions';

 const HomePage = Loadable({
    loader: () => import('./pages/home/'),
    loading: Loading,
    delay: 100
 });
 .....

 const Footer = LoadableVisibility({
    loader: () => import('./pages/common/footer/'),
    loading: Loading,
 })

 const Root = ({ store }) => {
return(
    <Provider store={store}>
            <div className="App">
                <Router> 
                    <div>
                        <Header />
                        <Switch>
                            <Route path="/resetting/reset/:token" component={PasswordResetHandlerPage} />
                            <Route path="/registration-result/:status" component={RegistrationResultPage} />
                            <PrivateRoute path="/account" component={AccountPage} />
                            <Route path="/logout" component={Logout} />
                            <Route path="/login" render={() => <div>login route</div>} />
                            <PrivateRoute path="/admin" component={AdminPage} />
                            <Route path="/search/:root?/:category?/:subcategory?/:place?" component={SearchPage} />
                            <Route path="/adview/:ad_id" component={AdviewPage} />
                            <PrivateRoute path="/offer/:ad_id/:offer_id?" component={OfferPage}/>
                            <PrivateRoute path="/mailbox/:type?/:message_id?/:ad_id?" component={MailboxPage}/>
                            <Route path="/" component={HomePage} />
                        </Switch>
                        <Footer />
                    </div>
                </Router>
            </div>
 </Provider>
 )
}

Typescript Typings

Hi there,

would you be interested in a PR that adds TypeScript typings?

Best,
Daniel

Enable setting minHeight and minWidth on placeholder div

Do you want to request a feature or report a bug?
feature

What is the current behaviour?

Defaults to 1px for minWidth and minHeight.

Desired behaviour

It would be a nice feature to be able to set the minWidth and minHeight by sending in arguments. Using a className does not suffice in all use cases.

I would be happy to contribute to this feature if this is something you would like to add to the library.

Use the 'isIntersecting' attribute as a fallback for zero threshold

There is a bug in determining an intersection when the threshold option is missing or equal to 0(which is the default value according to specs).

Actual result:

  1. Since in Chrome(Version 70.0.3538.110 and previous builds as well) intersection event for a target element fires only once with an intersectionRatio equal to 0 - wrapped component never get loaded;
  2. As a solution to this problem, I would suggest using isIntersecting attribute which is true if the target intersects with the root. I have already tested that code and it gives the result for an entry: isIntersecting === true && intersectionRatio === 0;

Expected result:

  1. Elements with missing or zero thresholds should be loaded correctly - it could be useful for lazy loading images;
  2. The fix should be quite simple though -
if (trackedElement &&
  (entry.isIntersecting || entry.intersectionRatio > 0)
) {
  trackedElement.visibilityHandler()
}

Always render all contents during SSR?

Inspecting the raw HTML returned from SSR, it seems this library doesn't return the not-yet-visible content (e.g. Footer) as part of SSR response. In our use case, we need to render all LoadableVisibility-wrapped components during SSR. Is it currently possible?

window is not defined on server side

capacities.js
var IntersectionObserver = exports.IntersectionObserver = window && window.IntersectionObserver;
on server side will throw error window is not defined
Can you change to this?
var IntersectionObserver = exports.IntersectionObserver = typeof window !== 'undefined' && window.IntersectionObserver;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.