React Infinite scrolling function by online API - javascript

I'm using YTS API and I would like to make Infinite scrolling function.
There is a page parameter and limit parameter. It seems it can work with them but I have no idea of how to use it. I'm a beginner user of React. Could you guys help me? Thanks in advance.
fetch('https://yts.am/api/v2/list_movies.json?sort_by=download_count&limit=20')
fetch('https://yts.am/api/v2/list_movies.json?sort_by=download_count&page=2')
This is the link of YTS API https://yts.am/api#list_movies

I would try using React-Waypoint and dispatch an action to fetch the data every time it enters the screen.
The best way IMO is using redux but here's an example without:
state = { currentPage: 0, data: [] };
getNextPage = () => {
fetch(`https://yts.am/api/v2/list_movies.json?sort_by=download_count&page=${this.state.currentPage}`).
then((res) => this.setState((prevState) => ({currentPage: prevState.currentPage + 1, data: res.body}));
}
render(){
<div>
{
this.state.data.map((currentData) => <div>{currentData}</div>)
}
<Waypoint onEnter={this.getNextPage}/>
</div>
}

I would like to show {this._renderList() } infinitely
import React, {Component} from 'react';
import L_MovieList from './L_MovieList';
import L_Ranking from './L_Ranking';
import './L_Right_List.css';
import Waypoint from 'react-waypoint';
class L_BoxOffice extends Component {
state = {
currentPage: 0,
data : []
};
constructor(props) {
super(props);
this.state = {
movies: []
}
this._renderRankings = this._renderRankings.bind(this);
this._renderList = this._renderList.bind(this);
}
componentWillMount() {
this._getMovies();
}
_renderRankings = () => {
const movies = this.state.movies.map((movie, i) => {
console.log(movie)
return <L_Ranking title={movie.title_english} key={movie.id} genres={movie.genres} index={i}/>
})
return movies
}
_renderList = () => {
fetch(`https://yts.am/api/v2/list_movies.json?sort_by=download_count&page=${this.state.currentPage}`)
.then((res) => this.setState((prevState) => ({currentPage: prevState.currentPage + 1, data: res.body}));
const movies = this.state.movies.map((movie) => {
console.log(movie)
return <L_MovieList title={movie.title_english} poster={movie.medium_cover_image} key={movie.id} genres={movie.genres} language={movie.language} runtime={movie.runtime} year={movie.year} rating={movie.rating} likes={movie.likes} trcode={movie.yt_trailer_code}/>
})
return movies
}
_getMovies = async () => {
const movies = await this._callApi()
this.setState({
movies
})
}
_callApi = () => {
return fetch('https://yts.am/api/v2/list_movies.json?sort_by=download_count&limit=10').then(potato => potato.json())
.then(json => json.data.movies)
.catch(err => console.log(err))
}
getNextPage = () => {
fetch(`https://yts.am/api/v2/list_movies.json?sort_by=download_count&page=${this.state.currentPage}`).
then((res) => this.setState((prevState) => ({currentPage: prevState.currentPage + 1, data: res.body}));
}
render() {
const {movies} = this.state;
let sub_title;
let right_information;
if (this.props.page == 'main') {
sub_title = <div>Today Box Office</div>;
right_information = <div>
aaa</div>;
} else if (this.props.page == 'box_office') {
right_information = <div className={movies
? "L_Right_List"
: "L_Right_List--loading"}>
{this._renderList()}
{
this.state.data.map((currentData) => <div>{this._renderList()}</div>)
}
<Waypoint onEnter={this.getNextPage}/>
</div>;
}
return (<div style={{
backgroundColor: '#E5E5E5',
paddingTop: '20px',
paddingLeft: '20px'
}}>
{sub_title}
<div className={movies
? "L_Left_Ranking"
: "L_Left_Ranking--loading"}>
<div className="L_Left_Ranking_title">영화랭킹</div>
{this._renderRankings()}
</div>
{right_information}
</div>);
}
}
export default L_BoxOffice;

Related

How do I convert Class Components to Functional Components in React.js project?

In the project I watch, they work with class component, but I want to do these operations with functional component using hooks. How can you help me guys? I tried many times but couldn't do this translation. I'm still trying
My code (imported data is "ingredients"):
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
ingredients: [],
totalPrice: 0
}
this.addIngredients = this.addIngredients.bind(this)
this.removeIngredients = this.removeIngredients.bind(this)
this.calculateTotal = this.calculateTotal.bind(this)
}
addIngredients(product) {
this.setState({
ingredients: [...this.state.ingredients].concat([
{ ...product, displayId: Math.random() }
])
})
}
removeIngredients(product) {
const selectedProduct = this.state.ingredients.find((ingredient) => {
return ingredient.name === product.name
})
const targetId = selectedProduct.displayId
this.setState({
ingredients: this.state.ingredients.filter((ingredient) => {
return ingredient.displayId !== targetId
})
})
}
calculateTotal() {
let total = 4
this.state.ingredients.forEach((item) => {
total += item.price
})
return total.toFixed(2)
}
render() {
return (
<div>
<Hamburger ingredients={this.state.ingredients} />
<TotalPrice total={this.calculateTotal} />
<ItemList
items={ingrediends}
addIngredients={this.addIngredients}
removeIngredients={this.removeIngredients}
selectedIngredients={this.state.ingredients}
/>
</div>
)
}
}
export default App
Navarrro I hope this helps you! I couldn't test it but Is a good started for you, I use ES6 syntax...
import React, { useState } from 'react';
import { Hamburger, TotalPrice, ItemList } from './SuperComponents.jsx';
const App = () => {
const [ingredients, setIngredients] = useState([]);
// You are not using this state
// const [totalPrice, setTotalPrice] = useState(0);
const addIngredients = (product) => {
setIngredients([...ingredients, { ...product, displayId: Math.random() }]);
};
const removeIngredients = (product) => {
const selectedProduct = ingredients.find(
(ingredient) => ingredient.name === product.name
);
const { targetId } = selectedProduct;
setIngredients(
ingredients.filter((ingredient) => ingredient.displayId !== targetId)
);
};
const calculateTotal = () => {
let total = 4;
ingredients.forEach((item) => (total += item.price));
return total.toFixed(2);
};
return (
<>
<Hamburger ingredients={ingredients} />
<TotalPrice total={() => calculateTotal()} />
<ItemList
items={ingredients}
addIngredients={(i) => addIngredients(i)}
removeIngredients={(i) => removeIngredients(i)}
selectedIngredients={ingredients}
/>
</>
);
};
export default App;

React Unmounted Component State Update Error

I have the following components in react:
PublicProfile.js:
import React, { Component } from 'react';
import axios from 'axios'
import Post from '../posts/Post'
import Navbar from '../Navbar'
import FollowButton from './FollowButton'
import { Avatar, Button, CircularProgress } from '#material-ui/core'
class PublicProfile extends Component {
constructor(props) {
super(props);
this.state = {
user: {},
followers: undefined,
following: undefined,
posts: [],
showFollowers: false,
showFollows: false,
curr_id: null
}
this.handleFollowerClick = this.handleFollowerClick.bind(this)
this.handleFollowClick = this.handleFollowClick.bind(this)
}
componentDidMount() {
const { user_id } = this.props.match.params
axios.get(`http://127.0.0.1:8000/users/${user_id}`)
.then(res =>
this.setState({
user: res.data,
followers: res.data.followers.length,
following: res.data.following.length
}))
.catch(err => console.log(err))
axios.get(`http://127.0.0.1:8000/posts/user/${user_id}`)
.then(res => {
this.setState({ posts: res.data })
})
.catch(err => console.log(err))
axios.get('http://127.0.0.1:8000/users/self')
.then(res => this.setState({curr_id: res.data.id}))
.catch(err => console.log(err))
}
handleFollowerClick(e) {
e.preventDefault()
if (this.state.showFollowers === true) {
this.setState({showFollowers: false})
} else {
this.setState({showFollowers: true})
}
}
handleFollowClick(e) {
e.preventDefault()
if (this.state.showFollows === true) {
this.setState({showFollows: false})
} else {
this.setState({showFollows: true})
}
}
render() {
const showFollowers = this.state.showFollowers
const showFollows = this.state.showFollows
let followers
let follows
let edit
let fbutton
if (showFollowers === true) {
followers = (
<div>
<p>Followed by:</p>
<ul>
{this.state.user.followers.map(follower => (
<li key={follower.id}><a href={`/users/${follower.user.id}`}>{follower.user.username}</a></li>
))}
</ul>
</div>
)
}
if (showFollows === true) {
follows = (
<div>
<p>Follows:</p>
<ul>
{this.state.user.following.map(follow => (
<li key={follow.id}><a href={`/users/${follow.user.id}`}>{follow.user.username}</a></li>
))}
</ul>
</div>
)
}
if (this.state.user.id === this.state.curr_id) {
edit = <Button href='/profile'>Edit My Profile</Button>
}
if (this.state.user.id !== this.state.curr_id) {
fbutton = <FollowButton user={this.state.user} followers_num={this.state.followers} setParentState={state => this.setState(state)} />
}
if (this.state.user.id !== undefined) {
return (
<div style={{background: '#f7f4e9'}}>
<Navbar />
<div style={{height: '70px'}}></div>
<div>
<Avatar style={{width: 75, height: 75}} variant='rounded' src={this.state.user.pp} alt={this.state.user.username} />
<h1>{this.state.user.username}</h1>
<h3>#{this.state.user.username}</h3>
<h4>{this.state.posts.length} Post(s)</h4>
<p>{this.state.user.bio}</p>
<Button style={{marginLeft: '10px'}} disabled={!this.state.following} onClick={this.handleFollowClick}>{this.state.following} Follows</Button>
<Button disabled={!this.state.followers} onClick={this.handleFollowerClick}>{this.state.followers} Followers</Button>
{followers}
{follows}
</div>
{edit}
{fbutton}
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column'
}}>
{this.state.posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
<div style={{height: '15px'}}></div>
</div>
)
} else {
return (
<div>
<Navbar />
<CircularProgress />
</div>
)
}
}
}
export default PublicProfile
FollowButton.js:
import React, { Component } from 'react';
import axios from 'axios'
import { Button } from '#material-ui/core'
class FollowButton extends Component {
constructor(props) {
super(props);
this.state = {
followsUser: null
}
this.unfollowClick = this.unfollowClick.bind(this)
this.followClick = this.followClick.bind(this)
}
componentDidMount() {
if (this.props.user.id !== undefined) {
axios.get(`http://127.0.0.1:8000/users/check/${this.props.user.id}`)
.then(res => {
this.setState({ followsUser: res.data.follows })
})
.catch(err => console.log(err))
}
}
unfollowClick() {
axios.delete(`http://127.0.0.1:8000/users/${this.props.user.id}/unfollow/`)
.then(() => {
this.setState({ followsUser: false })
this.props.setParentState({followers: this.props.followers_num - 1})
})
.catch(err => console.log(err))
}
followClick() {
axios.post(`http://127.0.0.1:8000/users/${this.props.user.id}/follow/`)
.then(res => {
this.setState({ followsUser: true })
this.props.setParentState({followers: this.props.followers_num + 1})
})
.catch(err => console.log(err))
}
// user: {
// followers: [...this.props.user.followers, res.data.user]
// }
render() {
let button
if (this.state.followsUser) {
button = <Button style={{background: 'blue'}} onClick={this.unfollowClick}>Following</Button>
} else {
button = <Button onClick={this.followClick}>Follow</Button>
}
return (
<div style={{marginTop: '20px'}}>
{button}
</div>
);
}
}
But I get the following error:
index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in FollowButton (at PublicProfile.js:93)
I have found that this error is largely due to unresolved process when unmounting the component, but I am not even rendering the component in this case due to the conditional, but I still seem to get the error. Can someone please help me fix it.
You are not cancelling axios request when component unmounted. Axios accepts cancelToken as a parameter, you should create a CancelTokenSource which provides a method cancel and then cancel the Source when component unmounts which cancels all pending request.
Here's the syntax of how to use async/await:
const unfollowClick = async() => {
try{
const res = await axios.delete(`http://127.0.0.1:8000/users/${this.props.user.id}/unfollow/`);
this.setState({ followsUser: false });
this.props.setParentState({followers: this.props.followers_num - 1});
}
catch(err) { console.log(err)}
}

React Expected an assignment or function call and instead saw an expression

I'm trying to render the data from my database get this instead Failed to compile.
./src/components/list-pets.component.js
Line 38:5: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.enter code here
Here is my code from the trouble component
import React, { Component } from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: []
};
}
componentDidMount = () => {
this.getPets();
};
getPets = () => {
axios.get('http://localhost:5000/pets')
.then((response) => {
const data = response.data;
this.setState({ pets: data });
console.log('Data has been received!');
})
.catch((err) => {
console.log(err);
});
}
displayPet = (pets) => {
if (!pets.length) return null;
return pets.map((pet, index) => {
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
});
};
render() {
console.log('State: ', this.state);
return (
<div className='adopt'>
{this.displayPet(this.state.pets)}
</div>
)
}
}
You need to return a value at each pets.map iteration, currently you’re returning undefined.
return pets.map((pet, index) => {
return (
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
)
});
You have to wait until fetching data is completed.
You should have to define the loading bar while fetching.
class App extends Component {
constructor() {
super();
this.state = {
pageData: {},
loading: true
}
this.getData();
}
async getData(){
const res = await fetch('/pageData.json');
const data = await res.json();
return this.setState({
pageData: data,
loading: false
});
}
componentDidMount() {
this.getData();
}
render() {
const { loading, pageData } = this.state;
if (loading){
return <LoadingBar />
}
return (
<div className="App">
<Navbar />
</div>
);
}
}

Component did update returning always the same props and state

I found a lot of solutions about this problem but none of them work.
I have a view which renders dynamically components depending on the backend response
/**
* Module dependencies
*/
const React = require('react');
const Head = require('react-declarative-head');
const MY_COMPONENTS = {
text: require('../components/fields/Description'),
initiatives: require('../components/fields/Dropdown'),
vuln: require('../components/fields/Dropdown'),
severities: require('../components/fields/Dropdown'),
};
const request = restclient({
timeout: 5000,
baseURL: '/api',
});
const { DropdownItem } = Dropdown;
class CreateView extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
states: props.states,
error: props.error,
spinner: true,
state: props.state,
prevState: '',
components: [],
};
this.handleChange = this.handleChange.bind(this);
this.getRequiredFields = this.getRequiredFields.bind(this);
this.onChangeHandler = this.onChangeHandler.bind(this);
this.changeState = this.changeState.bind(this);
this.loadComponents = this.loadComponents.bind(this);
}
componentDidMount() {
this.loadComponents();
}
onChangeHandler(event, value) {
this.setState((prevState) => {
prevState.prevState = prevState.state;
prevState.state = value;
prevState.spinner = true;
return prevState;
}, () => {
this.getRequiredFields();
});
}
getRequiredFields() {
request.get('/transitions/fields', {
params: {
to: this.state.state,
from: this.state.prevState,
},
})
.then((response) => {
const pComponents = this.state.components.map(c => Object.assign({}, c));
pComponents.forEach((c) => {
c.field.required = 0;
c.field.show = false;
});
response.data.forEach((r) => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
ob.field.required = r.required;
ob.field.show = true;
}
});
this.setState({
components: pComponents,
fields: response.data,
spinner: false,
});
})
.catch(err => err);
}
loadComponents() {
this.setState((prevState) => {
prevState.components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
};
return {
field, component: MY_COMPONENTS[k],
};
});
return prevState;
});
}
handleChange(field, value) {
this.setState((prevState) => {
prevState[field] = value;
return prevState;
});
}
changeState(field, value) {
this.setState((prevState) => {
prevState[`${field}`] = value;
return prevState;
});
}
render() {
const Components = this.state.components;
return (
<Page name="CI" state={this.props} Components={Components}>
<Script src="vendor.js" />
<Card className="">
<div className="">
<div className="">
<Spinner
show={this.state.spinner}
/>
{Components.map((component, i) => {
const Comp = component.component;
return (<Comp
key={i}
value={this.state[component.field.name]}
field={component.field}
handleChange={this.handleChange}
modal={this.state.modal}
changeState={this.changeState}
/>);
})
}
</div>
</div>
</div>
</Card>
</Page>
);
}
}
module.exports = CreateView;
and the dropdown component
const React = require('react');
const request = restclient({
timeout: 5000,
baseURL: '/api',
});
const { DropdownItem } = Dropdown;
class DrpDwn extends React.Component {
constructor(props) {
super(props);
this.state = {
field: props.field,
values: [],
};
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('state', this.state.field);
console.log('prevState', prevState.field);
console.log('prevProps', prevProps.field);
console.log('props', this.props.field);
}
render() {
const { show } = this.props.field;
return (show && (
<div className="">
<Dropdown
className=""
onChange={(e, v) => this.props.handleChange(this.props.field.name, v)}
label={this.state.field.name.replace(/^./,
str => str.toUpperCase())}
name={this.state.field.name}
type="form"
value={this.props.value}
width={100}
position
>
{this.state.values.map(value => (<DropdownItem
key={value.id}
value={value.name}
primary={value.name.replace(/^./, str => str.toUpperCase())}
/>))
}
</Dropdown>
</div>
));
}
module.exports = DrpDwn;
The code actually works, it hide or show the components correctly but the thing is that i can't do anything inside componentdidupdate because the prevProps prevState and props are always the same.
I think the problem is that I'm mutating always the same object, but I could not find the way to do it.
What I have to do there is to fill the dropdown item.
Ps: The "real" code works, i adapt it in order to post it here.
React state is supposed to be immutable. Since you're mutating state, you break the ability to tell whether the state has changed. In particular, i think this is the main spot causing your problem:
this.setState((prevState) => {
prevState.components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
}; return {
field, component: MY_COMPONENTS[k],
};
});
return prevState;
});
You mutate the previous states to changes its components property. Instead, create a new state:
this.setState(prevState => {
const components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
};
return {
field, component: MY_COMPONENTS[k],
};
});
return { components }
}
You have an additional place where you're mutating state. I don't know if it's causing your particular problem, but it's worth mentioning anyway:
const pComponents = [].concat(this.state.components);
// const pComponents = [...this.state.components];
pComponents.forEach((c) => {
c.field.required = 0;
c.field.show = false;
});
response.data.forEach((r) => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
ob.field.required = r.required;
ob.field.show = true;
}
});
You do at make a copy of state.components, but this will only be a shallow copy. The array is a new array, but the objects inside the array are the old objects. So when you set ob.field.required, you are mutating the old state as well as the new.
If you want to change properties in the objects, you need to copy those objects at every level you're making a change. The spread syntax is usually the most succinct way to do this:
let pComponents = this.state.components.map(c => {
return {
...c,
field: {
...c.field,
required: 0,
show: false
}
}
});
response.data.forEach(r => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
// Here it's ok to mutate, but only because i already did the copying in the code above
ob.field.required = r.required;
ob.field.show = true;
}
})

How do I setState of object.item[n] nested within array which is nested within an array of objects

I have following data structure:
const library = [
{
procedureName:'Build Foundations',
id:1,
tasks:[
{
taskName:'dig at least 1m deep', isCompleted:false, procedureId:1
},
{
taskName:'At least 50m wide digging', isCompleted:false, procedureId:1
},
{
taskName:'Buy megazords', isCompleted:false, procedureId:1
}
]
},
{
procedureName:'Building the roof',
id:2,
tasks:[
{
taskName:'Constructed according to the project', isCompleted:false, procedureId:2
},
{
taskName:'Protect wood from humidity', isCompleted:false, procedureId:2
},
{
taskName:'Roof build with the correct angle', isCompleted:false, procedureId:2
}
]
}
]
I want my function onTaskToggle to setState of library.tasks.isCompleted like this: library.tasks.isCompleted: !library.tasks.isCompleted
(I want to be able to distinguish the click of certain item as well)
I have no idea how get to this state.
Should I build my data structure differently or is there something I am missing?
onTaskToggle = (task) => {
const findProcedure = library => library.filter(procedure => procedure.id === task.procedureId);
const findTask = tasks => tasks.filter(singleTask => singleTask.taskName === task.taskName);
const toggledTask = findTask(findProcedure(this.state.library)[0].tasks);
const procedureIndex = this.state.library.indexOf(findProcedure(this.state.library)[0]);
const toggledTaskIndex = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
const insideProcedure = this.state.library[procedureIndex];
const insideTask = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
this.setState({
// Stuck there ...library
})
}
I'm not sure if it is needed, but that's the way I'm passing the data:
// Main component
class App extends Component {
constructor(props){
super(props);
this.state = {
projects,
library,
}
}
onTaskToggle = (task) => {
const findProcedure = library => library.filter(procedure => procedure.id === task.procedureId);
const findTask = tasks => tasks.filter(singleTask => singleTask.taskName === task.taskName);
const toggledTask = findTask(findProcedure(this.state.library)[0].tasks);
const procedureIndex = this.state.library.indexOf(findProcedure(this.state.library)[0]);
const toggledTaskIndex = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
const insideProcedure = this.state.library[procedureIndex];
const insideTask = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
this.setState({
// ...library
})
}
render() {
const {projects, library} = this.state;
return (
<div>
<Procedures library={library} onTaskToggle={this.onTaskToggle}/>
</div>
);
}
}
export default class Procedures extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
{
<ProceduresList library={this.props.library} onTaskToggle={this.props.onTaskToggle}/>
}
</div>
);
}
}
//Mapping through procedures
const ProceduresList = props => {
const list = props.library.map(procedure => {
const { id, procedureName, tasks } = procedure;
return(
<div key={id}>
<h4>{procedureName} </h4>
<div><ListingTasks tasks={tasks} onTaskToggle={props.onTaskToggle}/></div>
</div>
)
})
return <div>{list}</div>
}
export default ProceduresList;
// Last Component where all the magic shall happen
const ListingTasks = (props) => {
const list = props.tasks.map(task => { // under this line goes mapping through procedures
const taskName = task.taskName;
return <div key={taskName} onClick={() => props.onTaskToggle(task)}>{taskName}</div>
})
return <div>{list}</div>
}
export default ListingTasks
onTaskToggle(task) {
const { library } = this.state
const procedure = Object.assign({}, library.find(proc => (proc.id === task.procedureId)))
const procedureIndex = library.findIndex(prec => prec.id === procedure.id)
procedure.tasks = procedure.tasks.map((tsk) => {
if (tsk.taskName === task.taskName) {
tsk.isCompleted = !tsk.isCompleted
}
return tsk;
})
library.splice(procedureIndex, 1, procedure)
this.setState({ library })
}

Categories

Resources