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

holder.js does not work when the page renders twice in React #225

Closed
jguo1002 opened this issue Jul 22, 2019 · 14 comments
Closed

holder.js does not work when the page renders twice in React #225

jguo1002 opened this issue Jul 22, 2019 · 14 comments

Comments

@jguo1002
Copy link

I am using holder.js in React with react-bootstrap. In index.html I added in <header>:
<script src="holder.js"></script>

In the component ProjectCard I wrote:
<Card.Img className="proj-card-img" variant="top" data-src="holder.js/100px180" />

This component is rendered using map() function

{Object.keys(this.state.projects).map(key=>
     <ProjectCard key={key} index={key} details={this.state.projects[key]}/>)}

It works nicely if I set state in ComponentWillMount(). However, if I update the state and the page renders twice (e.g. use firebase to syncState), holder.js will not work.

I have tried

<script>
    Holder.run({
        images:"MyClassName"
    })
</script>

but it didn't work either.

Did I write anything wrong? Thanks!

@jguo1002 jguo1002 changed the title holder.js does not work when the page render twice in React holder.js does not work when the page renders twice in React Jul 22, 2019
@imsky
Copy link
Owner

imsky commented Aug 12, 2019

@cindy100295 could you provide a fiddle or a Gist (or anything that works for you) of the page?

in the meantime, i think one thing you could try is change this:

Holder.run({
        images:"MyClassName"
    })

to this:

Holder.run({
        images:".MyClassName" // should be .proj-card-img if going by your comments
    })

note the . before the class name, as the images property needs to be a valid CSS selector

@jguo1002
Copy link
Author

My code in index.html:

<head>
  <script src="holder.js"></script>
  <script>
      Holder.run({
        images: '.proj-card-img'
      })
    </script>
</head>

In React Component:

<Card.Img className="proj-card-img" variant="top" data-src="holder.js/100px180" />

But this time it does not show anything.

Some background: I have five cards, two of them are loaded from a JSON file (local). Three of them I pulled from Firebase (remote). Every time the two local cards render first. Then the other three show up later. Holder.js work for the local two but not for the remote three.

If I wrote the code above, five cards have no placeholder images.
Maybe it is because when the component updates, index.html does not refresh? Or my code is wrong...

@imsky
Copy link
Owner

imsky commented Aug 13, 2019

could you try something like this instead:

componentDidMount: function () {
  requestAnimationFrame(function () {
    Holder.run({
      images: '.proj-card-img'
    });
  });
}

also, as a followup, you could try passing a Node (Element) to images which works better if you treat each placeholder as a component

@jguo1002
Copy link
Author

Thanks! I figure it out.
In index.html:

<script>
    function holder() {
      Holder.run({
        images: ". MyClassName"
      })
    }
</script>

In component:

componentDidMount() {
    window.holder();

And then it works.

@imsky
Copy link
Owner

imsky commented Oct 14, 2019

nice @cindy100295 i'll look into adding this to docs

@Senninseyi
Copy link

Senninseyi commented May 9, 2020

i used mine without the didmountComponent.... like this
import 'holderjs' { img: "holder.js/100px270", textHeader: "Well handled customers", link: "Our reviews on Reviews.co.uk", },
where it is being used

@nimbus2300
Copy link

I struggled to get holderjs 2.9.7 (https://www.npmjs.com/package/holderjs) working nicely in react bootstrap 1.4 (bootstrap 4.5.3).

I was trying to use it in a class component. In the end this worked:

yarn add holderjs && yarn install

Then in my component:

import { run as runHolder } from 'holderjs/holder';

class Foobar extends Component

 constructor(props) {
    ...
    this.runHolder = runHolder;
  }

 componentDidMount() {
   this.runHolder('image-class-name-no-initial-dot');
}

And on the image:

<Image src="holder.js/100x100"/>

@imsky
Copy link
Owner

imsky commented Jan 5, 2021

cool @nimbus2300 - glad you got it working. will keep that in mind when there's a native react component

@sersart
Copy link

sersart commented Jan 30, 2021

Hi,
you can run holderjs in React function without any problem. Please check example.

import React, { Fragment, useEffect } from "react";
import { Image } from "react-bootstrap";
import { run as runHolder } from 'holderjs/holder';

export function HolderExample() {
    useEffect(() => {        
        runHolder('image-class-name');
    });
    return (
         <Fragment>
           <Image 
                  className="image-class-name"
                  roundedCircle
                  src="holder.js/20x20?text=J&bg=28a745&fg=FFF"
            /> 
          </Fragment>
    );

Have fun.

yoieh added a commit to yoieh/DefinitelyTyped that referenced this issue Aug 11, 2021
According to imsky/holder#225 (comment) holderjs.run argumen can be a string of targeted element classes.
Don't know if this is a recent of holderjs change but this .d.ts dosen't work with holderjs as is and makes it unusable with a react ts setup.
@arnil6585
Copy link

yoieh:patch-1

typescript-bot pushed a commit to DefinitelyTyped/DefinitelyTyped that referenced this issue Aug 11, 2021
* Holderjs.run options can be a string

According to imsky/holder#225 (comment) holderjs.run argumen can be a string of targeted element classes.
Don't know if this is a recent of holderjs change but this .d.ts dosen't work with holderjs as is and makes it unusable with a react ts setup.

* extended holderjs test to just run with a string

* run() can alsow be empty
Made options optional

* Removed missleading options as string
@albertorodriguezdiaz
Copy link

Hi All.
You need to add:
1)
import { run as runHolder } from 'holderjs/holder';
2)
useEffect(() => { runHolder('image-class-name'); });
It's all 😃
imagen

@nimbus2300
Copy link

Hi All. You need to add: 1) import { run as runHolder } from 'holderjs/holder'; 2) useEffect(() => { runHolder('image-class-name'); }); It's all 😃 imagen

Assuming you're working with functional components, yes.

@gunslingor
Copy link

It only seems to work when I go to a URL, but when I use the react router to change the content of div for example with a navbar, it doesn't load.

import Container from "react-bootstrap/Container";
import 'bootstrap/dist/css/bootstrap.min.css';
import 'holderjs/holder';

class ProductList extends React.Component {
    render() {
        return (
            <Container>
                <Row xs={1} md={2} className="g-4">
                    <Col>
                        <Card>
                            <Card.Img variant="top" src="holder.js/100px160" />
                            <Card.Body>
                                <Card.Title>Card title</Card.Title>
                                <Card.Text>
                                    This is a longer card with supporting text below as a natural
                                    lead-in to additional content. This content is a little bit
                                    longer.asdfff
                                </Card.Text>
                            </Card.Body>
                        </Card>
                    </Col>
                </Row>
            </Container>
        );
    }
}
class Routing extends React.Component {
    render() {
        return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={ <Layout /> }>
                    <Route path="Home" element={ <Home /> } />
                    <Route path="Shop" element={ <Shop /> } />
                    <Route path="About" element={ <About /> } />
                    <Route path="Contact" element={ <Contact /> } />
                </Route>
                <Route path="*" element={<Navigate to="/Home" />} />
            </Routes>
        </BrowserRouter>
        )
    }
}

@gunslingor
Copy link

@nimbus2300 Thanks that solve it for me eventually. I think I understand it too, learning... was confused by runHolder... it's really run holder.js not a holder of run operations, lol, was confused. One thing I can't understand and google can't find is how this image-class-name-no-initial-dot class or 'userOptions' argument works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants