4

I'm trying to implement a loader to my react component, when the background image is loading it should display 'loading' and then once it has loaded it should display 'loaded'

I have a setTimeout() on my componentwillMount() to test that the loader functions as expected which it does

I'm struggling to understand how it knows when the image is loaded and to change the loading state

Is it best to put the image into a seperate component with the loader rather than have it on the Hello component?

https://www.webpackbin.com/bins/-KsOEkf9ubvR6iz4bMxG

Update

I have managed to get a simple image loader working using the onload() method attached to the image - https://www.webpackbin.com/bins/-KsNpZPzveUoby1yriFo

Hello.js

 import React from 'react'
import Loader from './Loader'
import styled from 'styled-components'

const Card = styled.div`
  height: 400px;
  width:20%;
  background: url('https://www.planwallpaper.com/static/images/Light-Wood-Background-Wallpaper.jpg');
`
export default class Test extends React.Component {
  constructor() {
    super()
    this.state = { loading:true}
  }

  componentWillMount()
  {
     setTimeout(() => this.setState({loading: false}), 3000)
      console.log("componentDidMount");
  }

  render() {
    return (
      <div>
        <Card>
        <Loader loading={this.state.loading} />
        </Card>
      </div>
    )
  }
}

Loader.js

import React, { Component } from 'react'
import styled, { keyframes } from 'styled-components'
import { string } from 'prop-types'

const transition1 = keyframes`
  0%     { background: #F19939; }
  33.33% { background: #F8CA8F; }
  66.66% { background: #FBD8AE; }
  100%   { background: #F19939; }
`

const transition2 = keyframes`
  0%     { background: #FBD8AE; }
  33.33% { background: #F19939; }
  66.66% { background: #F8CA8F; }
  100%   { background: #FBD8AE; }
`

const transition3 = keyframes`
  0%     { background: #F8CA8F; }
  33.33% { background: #FBD8AE; }
  66.66% { background: #F19939; }
  100%   { background: #F8CA8F; }
`

const Box = styled.span`
  height: 12px;
  width: 12px;
  margin: 0 3px 0 3px;
  border-radius: 4px;
  animation: 0.4s ${transition1 } infinite;
`

const Box2 = styled(Box)`
  animation: 0.4s ${transition2 } infinite;
`

const Box3 = styled(Box)`
  animation: 0.4s ${transition3 } infinite;
`

const TextWrap = styled.div`
  display: flex;
  flex: 0 0 100%;
  justify-content: center;
  color: #fff;
`

const Para = styled.p`
  color: #fff
  padding: 19px 0 0 0;
`

const ParaLoaded = styled(Para)`
  color: #fff;
  padding: 22px 0 0 0;
`

export default class Loader extends Component {

  render() {
    return (
      <div >
        <div >

          <TextWrap>
            {
              this.props.loading
                ?
                  <Para>Loading...</Para>
                :
                  <ParaLoaded>Loaded</ParaLoaded>
            }
          </TextWrap>

        </div>
      </div>
    )
  }
}

1 Answer 1

1

You can do it like this: https://www.webpackbin.com/bins/-KsOJpBVfpazfXghJAaF

LoadBackgroundImage.js

const LoadBackgroundImage = (component, imageUrl, seconds, success, failure) => {
  let timeoutOccured = false;
  const image = new Image();
  const timeout = setTimeout(() => {
    timeoutOccured = true;
    failure();
  }, seconds * 1000);

  image.onload = () => {
    clearTimeout(timeout);
    component.style.backgroundImage = `url('${imageUrl}')`;
    if (!timeoutOccured) success();
  };
  image.src = imageUrl;
};

export default LoadBackgroundImage;

Hello.js

import React from 'react'
import Loader from './Loader'
import styled from 'styled-components'
import LoadBackgroundImage from './LoadBackgroundImage'

const Card = styled.div`
  height: 400px;
  width:20%;
`
export default class Hello extends React.Component {
  constructor() {
    super()
    this.state = { loading:true}
  }

  componentDidMount() {
    LoadBackgroundImage(
      this.card,
      'https://www.planwallpaper.com/static/images/Light-Wood-Background-Wallpaper.jpg',
      5,
      () => this.setState({ loading: false }),
      () => console.log('The image did not load in 5 seconds')
    );
  }

  render() {
    return (
      <div>
        <Card innerRef={card => this.card = card}>
          <Loader loading={this.state.loading} />
        </Card>
      </div>
    )
  }
}

In render() you are using innerRef to obtain a reference to the card component and save it in this.card. Then in componentDidMount you are using this reference and LoadBackgroundImage function to load the image and monitor when it's loaded. If the image is loaded in given number of second success callback will be called, otherwise failure callback will be called. The image can still load after 5 seconds, but the success callback will not be called. If you want to be called anyway, you can skip this ckeck: if (!timeoutOccured) in LoadBackgroundImage function.

Sign up to request clarification or add additional context in comments.

2 Comments

this looks nice, could you also use this to check if a component is loading?
@tomharrison, I am not sure if there is a way to check if background image is loading when you set it directly with background: url(...). Probably not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.