I am writing a ponent that pulls gifs from Giphy and renders them inside a React Infinite Scroll Component. The infinite scroll is worked as expected, but then I ran into a problem. I want the gifs to auto play, so I added the autoplay attribute. I then realised that with all of the gifs playing at once the performance just dies.
I have tried a number of different solutions, including attaching a ref to each video ponent and using react-visibility-sensor to try to play when visible, but I have not been able to work out a way that works.
This is the ponent as it currently stands with each mp4 autoplaying. I have removed the VisibilitySensor because I could not get it working.
const videoRefs = useRef(
[...new Array(gifArray.length)].map(() => createRef()),
);
return (
<div>
{gifArray && (
<InfiniteScroll
dataLength={gifArray}
next={getMore}
hasMore={true}
loader={<div>Loading...</div>}
>
{gifArray.map((gif, i) => {
return (
<video
width='320'
height='240'
loop
autoPlay
key={gif.id}
ref={videoRefs.current[i]}
>
<source src={gif.images.downsized_small.mp4} type='video/mp4' />
</video>
);
})}
</InfiniteScroll>
)}
</div>
);
};
The data is retrieved in a seperate ponent and passed as props.
export const HomePage = () => {
const [gifObject, setGifObject] = useState([]);
const [searchValue, setSearchValue] = useState('');
const [pageNumber, setPageNumber] = useState(0);
const handleSearch = (value) => {
setSearchValue(value);
fetch(
`=${value}&api_key=${API_KEY}&limit=20`,
)
.then((data) => data.json())
.then((result) => {
setGifObject(result);
});
};
return (
<div>
<SearchForm handleSearchValue={handleSearch} />
{gifObject?.data?.length > 1 ? (
<GifDisplay gifArray={gifObject.data} getMore={getData} />
) : null}
</div>
);
}
I am writing a ponent that pulls gifs from Giphy and renders them inside a React Infinite Scroll Component. The infinite scroll is worked as expected, but then I ran into a problem. I want the gifs to auto play, so I added the autoplay attribute. I then realised that with all of the gifs playing at once the performance just dies.
I have tried a number of different solutions, including attaching a ref to each video ponent and using react-visibility-sensor to try to play when visible, but I have not been able to work out a way that works.
This is the ponent as it currently stands with each mp4 autoplaying. I have removed the VisibilitySensor because I could not get it working.
const videoRefs = useRef(
[...new Array(gifArray.length)].map(() => createRef()),
);
return (
<div>
{gifArray && (
<InfiniteScroll
dataLength={gifArray}
next={getMore}
hasMore={true}
loader={<div>Loading...</div>}
>
{gifArray.map((gif, i) => {
return (
<video
width='320'
height='240'
loop
autoPlay
key={gif.id}
ref={videoRefs.current[i]}
>
<source src={gif.images.downsized_small.mp4} type='video/mp4' />
</video>
);
})}
</InfiniteScroll>
)}
</div>
);
};
The data is retrieved in a seperate ponent and passed as props.
export const HomePage = () => {
const [gifObject, setGifObject] = useState([]);
const [searchValue, setSearchValue] = useState('');
const [pageNumber, setPageNumber] = useState(0);
const handleSearch = (value) => {
setSearchValue(value);
fetch(
`http://api.giphy./v1/gifs/search?q=${value}&api_key=${API_KEY}&limit=20`,
)
.then((data) => data.json())
.then((result) => {
setGifObject(result);
});
};
return (
<div>
<SearchForm handleSearchValue={handleSearch} />
{gifObject?.data?.length > 1 ? (
<GifDisplay gifArray={gifObject.data} getMore={getData} />
) : null}
</div>
);
}
Share
Improve this question
edited Jun 30, 2020 at 1:40
Tristan
asked Jun 30, 2020 at 0:30
TristanTristan
3714 silver badges19 bronze badges
2
- Hey, did you find any solutions for this? – Diego Fortes Commented Sep 10, 2020 at 10:20
- 1 @DiegoFortes I included my answer below – Tristan Commented Sep 15, 2020 at 4:10
3 Answers
Reset to default 6I ended up solving this one.
I created a ponent for each gif and used a package called react-visibility-sensor
to check if it was visible or not.
The GifDisplay
ponent remained pretty close to the same:
// GifDisplay.tsx
export const GifDisplay = ({ gifArray, getMore, handleSearch }) => {
return (
<GifDisplayWrapper>
{gifArray && (
<InfiniteScroll
dataLength={gifArray}
next={getMore}
hasMore={true}
loader={<div>Loading...</div>}
>
<ArrayWrapper>
{gifArray.map((gif) => {
return (
<div key={gif.id}>
<SingleGif gif={gif} />
</div>
);
})}
</ArrayWrapper>
</InfiniteScroll>
)}
</GifDisplayWrapper>
);
};
The SingleGif
ponent used react-visibility-sensor
and updated the state of each ponent.
// SingleGif.tsx
import VisibilitySensor from 'react-visibility-sensor';
export const SingleGif = ({ gif }) => {
const videoRef = useRef(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (isVisible) {
videoRef.current.play();
} else {
if (videoRef.current.play) {
videoRef.current.pause();
}
}
}, [isVisible]);
return (
<VisibilitySensor onChange={(isVisible) => setIsVisible(isVisible)}>
<video width='320' height='240' loop ref={videoRef}>
<source src={gif.images.downsized_small.mp4} type='video/mp4' />
</video>
</VisibilitySensor>
);
};
Use react-intersection-observer
instead of react-visibility-sensor
as above mented by someone. react-visibility-sensor
is no more available for this.
With react-intersection-observer
you just have to make small adjustments to the SingleGif
ponent.
const videoRef = useRef<HTMLVideoElement>(null);
const { ref, inView, entry } = useInView({
/* Optional options */
threshold: 0,
});
return(
<div ref={ref}>
<video
ref={videoRef}
...
/>
</div>
);