Show a Simple Animation on setState in react - reactjs

I display a data from JSON file to the DIV in my component.
I Set timeout for few seconds and after that the data displays.
I want to show a simple animation when the state changes to true.
Here is a sample of my Code Structure:
import someData from "../Data";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = { contentLoad: false }
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
})
}, 2500)
}
render() {
return (
<div>
{someData.map((someData) => {
return (
<div> {this.state.contentLoad && someData.name}</div>
)
})}
</div>
)
}
}
I read about react transition group, but cant understand cause i'm new to react. someone please use this as a template and provide me a codepen or codesandbox link for solution.

I agree with #JohnRuddell that Pose is a heavy library if all you need is a simple fade in. However I would look it if you are doing multiple animations that are more complex.
Sample code:
import React from "react";
import ReactDOM from "react-dom";
import posed from "react-pose";
import "./styles.css";
const AnimatedDiv = posed.div({
hidden: { opacity: 0 },
visible: { opacity: 1 }
});
class Example2 extends React.Component {
constructor(props) {
super(props);
this.state = { contentLoad: false };
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
});
}, 2500);
}
someData = ["hello", "hi", "test"];
render() {
return (
<AnimatedDiv pose={this.state.contentLoad ? "visible" : "hidden"}>
{this.someData.map(t => {
return <div>{t}</div>;
})}
</AnimatedDiv>
);
}
}
ReactDOM.render(<Example2 />, document.getElementById("root"));
Sandbox link

Related

Child component not triggering rendering when parent injects props with differnt values

Here are my components:
App component:
import logo from './logo.svg';
import {Component} from 'react';
import './App.css';
import {MonsterCardList} from './components/monster-list/monster-card-list.component'
import {Search} from './components/search/search.component'
class App extends Component
{
constructor()
{
super();
this.state = {searchText:""}
}
render()
{
console.log("repainting App component");
return (
<div className="App">
<main>
<h1 className="app-title">Monster List</h1>
<Search callback={this._searchChanged}></Search>
<MonsterCardList filter={this.state.searchText}></MonsterCardList>
</main>
</div>
);
}
_searchChanged(newText)
{
console.log("Setting state. new text: "+newText);
this.setState({searchText:newText}, () => console.log(this.state));
}
}
export default App;
Card List component:
export class MonsterCardList extends Component
{
constructor(props)
{
super(props);
this.state = {data:[]};
}
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
_loadData(monsterCardCount)
{
fetch("https://jsonplaceholder.typicode.com/users", {
method: 'GET',
}).then( response =>{
if(response.ok)
{
console.log(response.status);
response.json().then(data => {
let convertedData = data.map( ( el, index) => {
return {url:`https://robohash.org/${index}.png?size=100x100`, name:el.name, email:el.email}
});
console.log(convertedData);
this.setState({data:convertedData});
});
}
else
console.log("Error: "+response.status+" -> "+response.statusText);
/*let data = response.json().value;
*/
}).catch(e => {
console.log("Error: "+e);
});
}
render()
{
console.log("filter:" + this.props.filter);
return (
<div className="monster-card-list">
{this.state.data.map((element,index) => {
if(!this.props.filter || element.email.includes(this.props.filter))
return <MonsterCard cardData={element} key={index}></MonsterCard>;
})}
</div>
);
}
}
Card component:
import {Component} from "react"
import './monster-card.component.css'
export class MonsterCard extends Component
{
constructor(props)
{
super(props);
}
render()
{
return (
<div className="monster-card">
<img className="monster-card-img" src={this.props.cardData.url}></img>
<h3 className="monster-card-name">{this.props.cardData.name}</h3>
<h3 className="monster-card-email">{this.props.cardData.email}</h3>
</div>
);
}
}
Search component:
import {Component} from "react"
export class Search extends Component
{
_searchChangedCallback = null;
constructor(props)
{
super();
this._searchChangedCallback = props.callback;
}
render()
{
return (
<input type="search" onChange={e=>this._searchChangedCallback(e.target.value)} placeholder="Search monsters"></input>
);
}
}
The problem is that I see how the text typed in the input flows to the App component correctly and the callback is called but, when the state is changed in the _searchChanged, the MonsterCardList seems not to re-render.
I saw you are using state filter in MonsterCardList component: filter:this.props.searchText.But you only pass a prop filter (filter={this.state.searchText}) in this component. So props searchTextis undefined.
I saw you don't need to use state filter. Replace this.state.filter by this.props.filter
_loadData will get called only once when the component is mounted for the first time in below code,
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
when you set state inside the constructor means it also sets this.state.filter for once. And state does not change when searchText props change and due to that no rerendering.
constructor(props)
{
super(props);
this.state = {data:[], filter:this.props.searchText};
}
If you need to rerender when props changes, use componentDidUpdate lifecycle hook
componentDidUpdate(prevProps)
{
if (this.props.searchText !== prevProps.searchText)
{
this._loadData();
}
}
Well, in the end I found what was happening. It wasn't a react related problem but a javascript one and it was related to this not been bound to App class inside the _searchChanged function.
I we bind it like this in the constructor:
this._searchChanged = this._searchChanged.bind(this);
or we just use and arrow function:
_searchChanged = (newText) =>
{
console.log("Setting state. new text: "+newText);
this.setState({filter:newText}, () => console.log(this.state));
}
Everything works as expected.

react change slide in slider after clicking a button

I have a Sidebar that I've implemented this way:
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
Then I have a component called ProjectSliderComponent that realizes the behaviour of a carousel. It is done this way:
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
render() {
let i = 0;
return (
<div>
<Slider ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}
The ProjectComponent component simply specifies the layout. This is the App.js file, where I load the projectslidercomponent and my sidebar. I do this:
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
render() {
return (
<div className="App">
<MySidebar>
</MySidebar>
<ProjectSliderComponent projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
What I need to do is to change the slide according to which button I clicked. What do I have to do? Is there something like passing the "instance" of the projectslidercomponent and call a method to change the slide? Or something else?
At the react-slick docs you can read about slickGoTo, which is a method of the Slider component.
Since you're already storing this.slider you can try to make it accessible in MySidebar and use it whenever changeSlide is called. Most likely you have to create a changeSlide method on top level and hand it as property to your components.
To sort this problem what you have to do is create a function in parent component which updates the state of the app component and once the state is updated it will re render app component and the new props are send to slider component which will tell which slider to show. Below is the code :-
In App.js
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true,
menuId: 0
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
ChangeSlide = (menuId) => {
this.setState({
menuId //Here you will receive which slide to show and according to that render slides in mySliderbar component
})
}
render() {
return (
<div className="App">
<MySidebar ChangeSlide={this.ChangeSlide} />
<ProjectSliderComponent menuId={this.state.menuId} projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
In mySlidebar
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
this.props.changeSlide(e.currentTarget.id)
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
In slider component, you have to listen when the new props are coming and according to that change the slide see componentWillReceiveProps
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
componentWillReceiveProps(nextProps) {
if(nextprops.menuId != this.props.menuId){
this.slider.slickGoTo(nextprops.menuId, true)//use slider instance to
//call the function to go to a particular slide.
}
}
render() {
let i = 0;
const { menuId } = this.props
return (
<div>
<Slider initialSlide={menuId} ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}

React: TypeError: this._modal.$el.on is not a function

So I'm new to react and I'm trying to use a boilerplate but I'm getting the following error in the chrome console. Sorry if this is a repeat question but I've tried to google it and found nothing. Thanks for the help in advance.
(Console)
TypeError: this._modal.$el.on is not a function
(index.js)
import React from 'react'
import UIkit from 'uikit'
export default class Modal extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
this._modal = UIkit.modal(this.refs.modal, {
container: false,
center: true
// stack: true
})
console.log(this._modal)
this._modal.$el.on('hide', () => {
this.props.onHide()
})
this._modal._callReady()
if(this.props.visible) {
this._modal.show()
}
}
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
if(visible) {
this._modal.show()
} else {
this._modal.hide()
}
}
}
render() {
return (
<div ref="modal">
{this.props.children}
</div>
)
}
}
It is recommended to handle your modal with your compoenent state and refs are usually used for DOM manipulation.
What you can do is to initialize your state:
constructor(props) {
super(props)
this.state= {
isOpen : false
}
}
in your componentWillReceiveProps:
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
this.setState({
isOpen: visible
})
}
}
and in your render method:
render() {
return (
<div ref="modal">
{this.state.isOpen && this.props.children}
</div>
)
}
Let me know if this issue still persists. Happy to help

ReactJS - Loading undesirable repeated data from the API

I'm fetching Data from a Dribbble API. It is almost succefully but it is loading 12 shots from the API, and after this it is occurring a problem by repeating the same 12 again and then it after this it is loading the others in a right way: 13,14,15...
The shots' return:
1,2,3,4,5,6,7,8,9,10,11,12.
OK RIGHT
1,2,3,4,5,6,7,8,9,10,11,12.
repetition problem!!! WRONG
13,14,15,16... OK RIGHT
What is the solution for this undesirable repetition?
Here is the code:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Waypoint from 'react-waypoint';
import Dribbble from './Dribbble';
export default class Dribbbles extends Component {
constructor(props) {
super(props);
this.state = { page: 1, shots: [] };
this.getShots = this.getShots.bind(this);
}
componentDidMount() {
this.getShots();
}
getShots() {
return $.getJSON('https://api.dribbble.com/v1/shots?page='+ this.state.page + '&access_token=ACCESS_TOKEN_HERE&callback=?')
.then((resp) => {
var newShots = this.state.shots.concat(resp.data);
this.setState({
page: this.state.page + 1,
shots: newShots
});
});
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
});
return (
<div>
<ul>{shots}</ul>
<Waypoint
onEnter={this.getShots}
/>
</div>
);
}
}
Why
I think the onEnter event from your Waypoint component is triggered unexpectedly, which has caused the getShots() function being called multiple times.
How
My suggestion is to specify a dataFecthed state to control whether the Waypoint component mount or not.Just like below:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Waypoint from 'react-waypoint'
import Dribbble from './Dribbble'
export default class Dribbbles extends Component {
constructor(props) {
super(props)
this.state = { page: 1, shots: [], dataFetched: true }
this.getShots = this.getShots.bind(this)
}
componentDidMount() {
this.getShots()
}
getShots() {
this.setState({
dataFetched: false,
})
return $.getJSON(
'https://api.dribbble.com/v1/shots?page=' +
this.state.page +
'&access_token=41ff524ebca5e8d0bf5d6f9f2c611c1b0d224a1975ce37579326872c1e7900b4&callback=?'
).then(resp => {
var newShots = this.state.shots.concat(resp.data)
this.setState({
page: this.state.page + 1,
shots: newShots,
dataFetched: true,
})
})
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
})
return (
<div>
<ul>{shots}</ul>
{this.state.dataFetched && <Waypoint onEnter={this.getShots} />}
</div>
)
}
}

Render Clappr player in ReactJS

I'm using Clappr player with ReactJS.
I want Clappr player component appear and destroy when I click to a toggle button. But it seems like when Clappr player is created, the entire page has reload (the toggle button dissapear and appear in a blink). So here is my code:
ClapprComponent.js
import React, { Component } from 'react'
import Clappr from 'clappr'
class ClapprComponent extends Component {
shouldComponentUpdate(nextProps) {
let changed = (nextProps.source != this.props.source)
if (changed) {
this.change(nextProps.source)
}
return false
}
componentDidMount() {
this.change(this.props.source)
}
componentWillUnmount() {
this.destroyPlayer()
}
destroyPlayer = () => {
if (this.player) {
this.player.destroy()
}
this.player = null
}
change = source => {
if (this.player) {
this.player.load(source)
return
}
const { id, width, height } = this.props
this.player = new Clappr.Player({
baseUrl: "/assets/clappr",
parent: `#${id}`,
source: source,
autoPlay: true,
width: width,
height: height
})
}
render() {
const { id } = this.props
return (
<div id={id}></div>
)
}
}
export default ClapprComponent
Video.js
import React, { Component } from 'react'
import { Clappr } from '../components'
class VideoList extends Component {
constructor() {
super()
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
const boxStyle = {
width: "640",
height: "360",
border: "2px solid",
margin: "0 auto"
}
return (
<div>
<div style={boxStyle}>
{this.state.isActive ?
<Clappr
id="video"
source="http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"
width="640"
height="360" />
: ''}
</div>
<button class="btn btn-primary" onClick={this.toggle.bind(this)}>Toggle</button>
</div>
)
}
}
export default VideoList
Anyone can explain why? And how to fix this problem?
Edit 1: I kind of understand why the button is reload. It's because in index.html <head>, I load some css. When the page is re-render, it load the css first, and then execute my app.min.js. The button doesn't reload in a blink if I move the css tags under the <script src="app.min.js"></script>.
But it doesn't solve my problem yet. Because the css files have to put in <head> tags. Any help? :(
Here you have a running (jsbin link) example. I simplified a little bit and it still shows your main requirement:
class ClapprComponent extends React.Component {
componentDidMount(){
const { id, source } = this.props;
this.clappr_player = new Clappr.Player({
parent: `#${id}`,
source: source
});
}
componentWillUnmount() {
this.clappr_player.destroy();
this.clappr_player = null;
}
render() {
const { id } = this.props;
return (
<div>
<p>Active</p>
<p id={id}></p>
</div>
);
}
}
class NotActive extends React.Component {
render() {
return (
<p>Not Active</p>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
return (
<div>
<h1>Clappr React Demo</h1>
{ this.state.isActive ?
<ClapprComponent
id="video"
source="http://www.html5videoplayer.net/videos/toystory.mp4"
/> :
<NotActive />}
<button onClick={this.toggle}>Toggle</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Also make sure to rename the button class property to className.
Maybe you can work from here to find your exact problem? Hope that helps.
In Clappr's documentation I found a like about how to use clappr with reactjs

Resources