Reveal icons on scroll in React.js - javascript

I am a beginner in React.js and have a problem. I am trying to reveal icons on scroll with a little delay of time for each icon. Something like that Example template. In this bootstrap template you can see when we scroll icons reveal (each icon with a little delay of time). Its possible with jquery scroll reveal module. But I don't know how to achive this with React.js. Is there anyway to do this in react.js using javascript only? Here is my react functional component code.
import React from 'react';
function Howitworks() {
return (
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">How It Works</h2>
<hr className="my-4 thick-hr-2" />
</div>
</div>
</div>
<div className="container text-center">
<div className="row">
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-home fa-4x icon-orange"></span>
<h3 className="my-3">Choose A Restaurant</h3>
</div>
</div>
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-utensils fa-4x icon-orange"></span>
<h3 className="my-3">Choose A Tasty Dish</h3>
</div>
</div>
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-shipping-fast fa-4x icon-orange"></span>
<h3 className="my-3">Pick Up Or Delivery</h3>
</div>
</div>
</div>
</div>
</div>
)
}
export default Howitworks;

Use Intersection Observer to observe when the containing div for the icons enters the viewport. Intersection Observer is vanilla JS, does not require any external modules or libraries, and is built for when elements are entering the viewport on scroll.
Here, I will make the container div easily targetable by giving it an id:
<div id="container-intersect" className="container text-center">
...
...
</div>
I then create a configuration object for IntersectionObserver:
// threshold controls how much of #container-intersect must
// be in view before firing the callback function. A value
// of 1.0 means that #container-intersect must be entirely
// in view. A value of 0.5 means that #container-intersect
// must be at least 50% in view.
var options = {
root: document.querySelector('body'),
rootMargin: '0',
threshold: 1.0
}
Then I create a new observer that fires the function callback when #container-intersect enters the viewport.
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#container-intersect');
observer.observe(target);
callback fires and fades in your elements.
var callback = function() {
let icons = document.querySelectorAll('.service-box span');
icons.forEach(function(icon, index) {
icons[index].style.opacity = '1';
});
};
You can place all of this code inside your componentDidMount() lifecycle function in your component, like so:
function Howitworks() {
componentDidMount() {
var options = {
root: document.querySelector('body'),
rootMargin: '0',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#container-intersect');
observer.observe(target);
var callback = function() {
let icons = document.querySelectorAll('.service-box span');
icons.forEach(function(icon, index) {
icons[index].style.opacity = '1';
});
};
}
render() {
return (
...
...
);
}

You can use this lib to detect the component is visible on screen.
Lib react-on-screen: https://github.com/fkhadra/react-on-screen
For use:
import React from 'react';
import TrackVisibility from 'react-on-screen';
const ComponentToTrack = ({ isVisible }) => {
const style = {
background: isVisible ? 'red' : 'blue'
};
return <div style={style}>Hello</div>;
}
const YourApp = () => {
return (
{/* Some Stuff */}
<TrackVisibility>
<ComponentToTrack />
</TrackVisibility>
{/* Some Stuff */}
);
}

Related

How to preview posts on single page

Hi,
this is my code in react js
I want to show my blog posts on single page on clicking read more button I pick this url from
news api and i want to show every pot by its specific id but i use url as id
import React, { Component } from "react";
import BlogPage from "./BlogPage";
import propTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";
export default class blogPost extends Component {
static defaultProps = {
category: "general",
};
static propTypes = {
category: propTypes.string,
};
constructor(props) {
super(props);
this.state = {
articles: [],
};
/* document.title = `${this.props.category}-news `; */
}
async componentDidMount() {
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
this.setState({
articles: parsData.articles,
totalResults: parsData.totalResults,
});
}
getMorePost = async () => {
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
this.setState({ articles: this.state.articles.concat(parsData.articles) });
};
render() {
return (
<div>
<div className="container">
{/* <span style={{ color: "grey" }}>
Blogs
<span style={{ fontWeight: "500", color: "black" }}>
- {this.props.category}
</span>
</span> */}
<InfiniteScroll
dataLength={this.state.articles.length}
next={getMorePost}
hasMore={this.state.articles.length !== this.state.totalResults}
loader={<h3> Loading...</h3>}
endMessage={<h4>Nothing more to show</h4>}
>
<div className="row pt-3.">
{this.state.articles.map((element) => {
return (
<div className={this.props.BlogClass} key={element.url}>
<BlogPage
CardClass={this.props.ClassCard}
ClassDes={this.props.DesClass}
ClassTitle={(this.props.TitleClass, " heading-blog")}
CardBody={this.props.BodyCard}
imgStyle={this.props.styleImg}
CardStyle={{
width: "100%",
height: "100%",
padding: "10px 0",
border: "none",
}}
title={element.title ? element.title.slice(0, 45) : ""}
description={
element.description
? element.description.slice(0, 90)
: ""
}
author={
/* element.author? element.author.slice(0, 5) : "" */
element.category
}
img={element.urlToImage}
/>
</div>
);
})}
</div>
</InfiniteScroll>
</div>
</div>
);
}
}
I want to show this posts on this page by clicking the read more button i try it several times but don't understand it
import React, { useState, useEffect } from "react";
import BlogPost from "./BlogPost";
export default function Blogpreview() {
/* const [blog,setBlog]=useState()
useEffect(() => {
let blog = articles.find((blog) => blog.id === parseInt(id));
if (blog) {
setBlog(blog);
}
}, []) */
/* const [blog,setBlog]=useState(null)
useState = async () =>{
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
setBlog({ articles: parsData.articles,
totalResults: parsData.totalResults
});
}
*/
return (
<div className="container-fluid">
<div className="col-6 BlogPreviewBox px-5 ">
<div className="d-flex justify-content-center">
<img className="display-blog-box my-5" src="/"></img>
</div>
<h1 className="blog-preview-heading">
Why invoicing are beneficial for your business{/* {blog.title} */}
</h1>
<div className="d-flex flex-column align-items-center">
<span className="text-center py-3">07 December 2021</span>
<button className="btn-blog-display">2 min read</button>
</div>
</div>
<div className="row display-blog-content-row">
{/* <div className="col-3">
{ <div>
<p >What is Olabooks?</p>
<div className="d-flex flex-column">
<span>How to add invoice in Olabooks?</span>
<span className="py-2">How to share invoice in Olabooks?</span>
</div>
<i className="far fa-heart"></i>
</div>}
</div> */}
<div className="col-6">
<div className="d-flex flex-column display-blog-text">
<div>
<span>
Businesses that adopt advanced technologies are more likely to
thrive in the long run because of the rapid response to modern
techniques and innovation. Many questions need to be taken into
account while adopting advanced technology, but most
importantly, you begin to think about the values it must provide
to you and your business. It is not an easy task toruna
business, especially in this tech-savvy world. Let's dig into
some commercial benefits of moving from pen and paper invoicing
to efficient e-invoicing that would make headway for your
business.
</span>
</div>
</div>
<div className="tag-display-blog">
<div>
<strong>By Olabooks</strong>
<span>author</span>
</div>
</div>
</div>
{/* <div className="col-3">
<div>
<span>Related</span>
</div>
<div className="displa-related-blog">
<h5 className="py-2">Why is e-invoicing beneficial for your business?</h5>
<span>
Business that adopt advanced technologies are more likely to thrive in the long run because of
</span>
<button className="btn-related-blog-display">9 min read</button>
</div>
</div> */}
</div>
</div>
);
}
I tried making API calls to the same URL, it seems that you are getting same API response in both cases (with and without pageSize param), due to which, your "read more" is actually not rendering any new content to the screen
(with the pageSize query parameter)
(without the pageSize parameter)
Also, it is better to not include your API key in the question

Getting attribute from element

I made an object to manage the candidate education in online marketplace
let education ={
tabs:{},
render:function(){
document.querySelector('#education-tabs').innerHTML = ''
let container =``;
Object.values(this.tabs).map((tab)=>{
container += `
<div class="education-tab-container">
<div class="education-tab-right">
<div class="big-title">
${tab.faculty}
</div>
<div class="medium-title">
${tab.department}
</div>
<div class="small-title">
${tab.university}
</div>
</div>
<div class="education-tab-left">
<div class="date">
${tab.from || ''} ${tab.to || ''}
</div>
<div class="close" data-id="${tab.id}">
<span>x</span>
</div>
</div>
</div>
`
})
document.querySelector('#education-tabs').innerHTML = container
},
deleteTab:function(id){
delete this.tabs[id];
this.render();
},
add:async function(payload){
this.tabs = {...this.tabs,[payload.id]:payload}
this.render();
}
}
This updates the UI every time I add a new tab to this object.
The problem is when I try to remove a tab: sometimes I get the data-id attribute of the tab that I made and sometimes it returns null.
here is my function
const triggerListener = ()=>{
$("#education-tabs").find(".education-tab-left .close").click((e)=>{
if(e.target.getAttribute('data-id')){
alert(e.target.getAttribute('data-id'))
}
});
so is there a way to solve this problem?

How to update upvote counter for individual elements instead of all of them at once with React

Newbie dev learning React.
I'm trying to create an upvote functionality to a blog post in React but when I click on the upvote button I'm upvoting all of the blog post cards at once instead of the individual card.
How can I fix this? I believe the issue may be in the way I'm setting setState? But I may be wrong and looking for help.
Thanks in advance!
====
class Posts extends Component {
state= {
points: 0
}
componentDidMount() {
this.props.fetchPosts()
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render() {
const postItems = this.props.posts.map((post, index) => (
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{post.title}</div>
<div className="meta"> {post.author}</div>
<div className="description">
<p>{post.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
))
return (
<div>
<br />
<h2 className="ui header">
<i className="pencil alternate icon"></i>
<div className="content">
Blog Feed
<div className="sub header">Create New Post!</div>
</div>
</h2>
{postItems}
</div>
)
}
}
You have a single component storing the "points" state for all your posts. To achieve the functionality you described, each post should be it's own component with it's own state.
class Post extends Component {
state = {
points: 0
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render = () =>
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{this.props.title}</div>
<div className="meta"> {this.props.author}</div>
<div className="description">
<p>{this.props.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
}
}
You are upvoting every card because you have only one counter. A separate counter should be defined for every card.
state = {}; // dictionary-a-like state structure
handleClick = (id) => () => {
this.setState((prevState) => ({
[id]: prevState[id] ? prevState[id] + 1 : 1, // check and increment counter
}));
}
onClick={this.handleClick(post.id)} // call function with post.id as argument
{this.state[post.id] || 0} Votes // display votes for every card
Note: I assumed that every card has it's own unique id, if not - index may come handy too.
You will need one counter for each post. Currently you only have a single counter for all posts, which means that they all display that same value.
The best way to achieve this would probably be to separate your post into its own component, and have that keep track of the counter.
The following solution uses a post ID (if you have it) to create a key in a stateful points object. Then, on click, you can add to the correct points key.
state = {
points: {}
}
handleClick = postId => {
this.setState({
points: {
...this.state.points,
[postId]: (this.state.points[postId] || 0) + 1
}
})
}
const postItems = this.props.posts.map((post, index) => (
...
<div className="extra content">
<i className="check icon"></i>
{this.state.points[post.id] || 0} Votes
</div>
<button
className="ui button"
type="submit"
onClick={() => this.handleClick(post.id)}
>
Add Point
</button>
...
)

How to modify an element through a different element's onClick in nextjs?

import Link from 'next/link';
function myClick(e){
console.log(e);
}
const Index = () => (
<section className="min-vh-100">
<div className="input_bar border rounded-maximum p-1 mx-1 bg-white d-flex">
<input className="myButton submit_bar text-black" placeholder="Insert your input…"/>
<Link href="#"><a onClick={myClick} className="input_icon"></a></Link>
</div>
</section>
);
I'm using nextjs and I'm trying to change a div's class through a click function in another div, I couldn't find examples of how to do that so I can understand how it works. Thank you.
Here is an example using refs:
import Link from 'next/link'
const Index = () => {
let myDiv = React.createRef()
function myClick() {
myDiv.current.classList.add('add-this-class')
}
return (
<section className="min-vh-100">
<div
ref={myDiv}
className="input_bar border rounded-maximum p-1 mx-1 bg-white d-flex"
>
<input
className="myButton submit_bar text-black"
placeholder="Insert your input…"
/>
<Link href="#">
<a onClick={myClick} className="input_icon" />
</Link>
</div>
</section>
)
}
To understand what I'm doing here. I'm creating a reference with this line:
let myDiv = React.createRef()
Then I assign it to the element I want to access, in the example I assign it to the div:
<div ref={myDiv} className="..." >
And on the onClick function I access the div and add a class:
myDiv.current.classList.add('add-this-class')
Let me know if this works for you. (If it did, thank Abderrahman)
I used hooks.
const Index = () => {
const [className, setClassName] = useState("")
const myClick = () => {
setClassName("some-class")
}
return (
<div>
<button onClick={myClick}>Click me</button>
<div className={className}>Classname gets changes</div>
</div>
)
}

Maximum update depth exceeded- React component error

I am trying to implement lazy loading in my react component. But whenever I update my state in reducer after calling the (loadMore) function it gives me this error.
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Here is my article rendering file where i am implementing lazy loading. I am not setState anywhere in the render. I am not able to understand where the error is.
This component is called in article container.
Below is the table.js component code-
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
loadMore = () => {
if (this.props.articleReducer.article_data.length > 0) {
this.props.dispatch(loadArticleDataApi(2, 'lazyLoad'));
}
}
render() {
return (
<div>
<InfiniteScroll pageStart={1} loadMore={this.loadMore} initialLoad={true}
hasMore={true} loader={< div className = "loader" key = {0}> Loading ...</div>}>
<div className="table">
<div className="container ">
<div className="show-grid row">
<div className="col-xs-4 head1">Title</div>
<div className="col-xs-2 head2">Pub All</div>
<div className="col-xs-2 head3">Publisher</div>
<div className="col-xs-2 head4">Rss</div>
<div className="col-xs-1 head5">AMP</div>
<div className="col-xs-1 head7">Publish</div>
</div>
{this.props.articleReducer.article_data.map((ele, index) => {
let hreff = `https://so.city/amp/delhi/${ele._id}.html`;
return (
<div className="show-grid row rowData" key={index}>
<div className="col-xs-4">{ele.title}</div>
<div className="col-xs-2">
<SwitchButtonPubAll checkedProp={ele.allFeedPublished}/>
</div>
<div className="col-xs-2 publisher">{ele.createdBy}</div>
<div className="col-xs-2">
<SwitchButton checkedProp={ele.rssCreated}/>
</div>
<div className="col-xs-1 amp">
<i className="fa fa-refresh" aria-hidden="true"></i>
<a href={hreff}>
<i className="fa fa-link" aria-hidden="true"></i>
</a>
</div>
<div className="col-xs-1">
<i
className={ele.published === 1
? "fa fa-eye eyee"
: "fa fa-eye-slash"}
aria-hidden="true"></i>
</div>
</div>
)
})}
</div>
</div>
</InfiniteScroll>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {dispatch};
}
export default connect(state => state, mapDispatchToProps)(Table);
I suspect that you are using React Infinite Scroller and this answer is based on that.
The problem is that you are setting hasMore={true}. When you scroll to the end of page and hasMore is true, the component request more data by call loadMore, the new data is the same as the previous one (so the page is not scrolled up) but hasMore is still true (it must be false to tells the component that there is no new data) so it calls loadMore again, and again,... and crash.
Solution: Provide a mechanic to check if there is new data available and pass it to hasMore

Categories