React persistent state with React Router 4 - javascript

My homepage is fetching data from the API, putting it to component's state and rendering elements based on this state.
When I navigate to a subpage and then I want to go to the homepage again the data is fetched again which causes unnecessary load. How to prevent that? How to ensure that when I click a link to the homepage or back button the data will load immediately?
import React, {Component} from 'react';
import axios from 'axios';
import _ from 'lodash';
import { Link } from 'react-router-dom';
import ContributorsTable from './ContributorsTable';
import LoadingScreen from './LoadingScreen';
export const API_KEY = 'api-key'
const org = `https://api.github.com/orgs/angular?${API_KEY}`;
const unorderedContributors = [];
let contributorsList = [];
const contributorPromises = [];
const contributorPropertiesPromises = [];
class GitHubLists extends Component {
constructor(props) {
super(props);
this.state = {
repos: [],
contributors: [],
isLoaded: false
};
}
componentDidMount() {
axios.get(org)
.then(res => {
let numberRepos = res.data.public_repos;
let pages = Math.ceil(numberRepos/100);
let tmpRepos = [...this.state.repos];
for(let page = 1; page <= pages; page++) {
axios.get(`https://api.github.com/orgs/angular/repos?page=${page}&per_page=100&${API_KEY}`)
.then(res => {
for(let i = 0; i < res.data.length; i++) {
tmpRepos.push(res.data[i]);
}
this.setState({repos: tmpRepos});
})
.then(() => {
this.state.repos.map(repo =>
contributorPromises.push(axios.get(`${repo.contributors_url}?per_page=100&${API_KEY}`)
.then(res => {
if(!res.headers.link) {
unorderedContributors.push(res.data);
}
else {
for(let page = 1; page <= 5; page++) {//5 pages because of github limitation - can be done by recursion checking if res.headers.link.includes('rel="next"')
contributorPromises.push(
axios.get(`${repo.contributors_url}?page=${page}&per_page=100&${API_KEY}`)
.then(res => unorderedContributors.push(res.data))
)
}
}
}))
);
Promise.all(contributorPromises).then(() => {
contributorsList = _.chain(unorderedContributors)
.flattenDeep()
.groupBy('id')
.map((group, id) => ({
id: parseInt(id, 10),
login: _.first(group).login,
contributions: _.sumBy(group, 'contributions'),
contributorFollowers: 0,
followers_url: _.first(group).followers_url,
contributorRepositories: 0,
repos_url: _.first(group).repos_url,
contributorGists: 0,
gists_url: _.first(group).gists_url,
avatar: _.first(group).avatar_url,
url: _.first(group).html_url
}))
.orderBy(['contributions'],['desc'])
.filter((item) => !isNaN(item.id))
.value();
this.setState({contributors: contributorsList})
})
.then(() => {
let tmpContributors = [...this.state.contributors];
tmpContributors.map(contributor => contributor.gists_url = (contributor.gists_url).slice(0, -10));
tmpContributors.map(contributor => {
return contributor.link =
<div>
<Link
to={{
pathname: `contributors/${contributor.login}`,
state: {
login: contributor.login,
id: contributor.id,
repos_url: contributor.repos_url,
avatar: contributor.avatar
}
}}
>
See profile
</Link>
</div>
});
const getContributorProperties = (propertyUrl, contributorProperty) => {
for (let i = 0; i < 10; i++) {
contributorPropertiesPromises.push(axios.get(`${tmpContributors[i][propertyUrl]}?per_page=100&${API_KEY}`)
.then(res => {
if(res.data.length > 100) {
tmpContributors[i][contributorProperty] = res.data.length;
}
else {
for(let page = 1; page <= 5; page++) {
axios.get(`${tmpContributors[i][propertyUrl]}?page=${page}&per_page=100&${API_KEY}`)
tmpContributors[i][contributorProperty] += res.data.length;
}
}
})
)
}
}
getContributorProperties('followers_url', 'contributorFollowers');
getContributorProperties('repos_url', 'contributorRepositories');
getContributorProperties('gists_url', 'contributorGists');
Promise.all(contributorPropertiesPromises)
.then(() => this.setState({contributors: tmpContributors, isLoaded: true}))
})
})
}
})
}
render() {
if(this.state.isLoaded) {
return <ContributorsTable data={this.state.contributors}/>;
}
else {
return <LoadingScreen />
}
}
}
export default GitHubLists;

Instead of keeping your state at the route level, keep it at the app level (or somewhere that persists across url changes / children mounting + unmounting like a redux store).
class App {
state = { data: null }
fetchData() {
callApi().then(data => this.setState({ data }))
}
render() {
return (
<Router>
<div>
<Route
path="/page"
component={props =>
<Page data={this.state.data} fetchData={this.fetchData} />
}
/>
</div>
</Router>
)
}
}
class Page {
componentDidMount() {
if (!this.props.data) this.props.fetchData()
}
render() { ... }
}
React docs have a spot on "lifting state". The same principle applies when using React Router: https://facebook.github.io/react/docs/lifting-state-up.html

Related

Setting state in react native freezes app

In a react native I am trying create a class component that loads a list of categories with a tag and an AWS S3 image attached.
If you look at this code the app freezes on the line:
this.setState({ categories });
But if I comment out that line the console logs that categories array just fine.
Please help any ideas why?
I have tried creating a functional component also but same this happens.
import React, { Component, setState } from "react";
import { View, StyleSheet } from "react-native";
import { isTablet } from "react-native-device-detection";
import { getRecipeCategories } from "../../api/recipeCategoriesApi";
import CategoryImageItem from "./CategoryImageItem";
import { getS3Image, deleteS3Image } from "../../utility/amplify";
class CategoryImagePicker extends Component {
state = {
categories: [],
};
async componentDidMount() {
console.log("loadCats");
let res = await fetch("http://192.168.1.4:4000/api/recipe_categories");
let categories = await res.json();
for (const category of categories) {
const imgUrl = await getS3Image(category.category_image);
category.imageUrl = imgUrl.split("?")[0];
}
console.log("returned");
this.setState({ categories });
console.log("set done...", categories);
}
loadViews() {
let i = 0;
let rowType = "odd";
let viewRows = [];
const { categories } = this.state;
console.log(categories.length);
while (i < categories.length) {
console.log("//", i);
if (isTablet) {
switch (rowType) {
case "odd":
if (i == categories.length - 1) {
viewRows.push(this.renderEvenRow(i, categories[i]));
} else {
viewRows.push(
this.renderOddRow(i, categories[i], categories[i + 1])
);
rowType = "even";
i += 2;
}
break;
case "even":
viewRows.push(this.renderEvenRow(i, categories[i]));
rowType = "odd";
i += 1;
break;
default:
break;
}
} else {
viewRows.push(this.renderEvenRow(i, categories[i]));
}
}
return viewRows;
}
handleShowRecipesForCategory = (category) => {
this.props.onShowRecipesForCategory(category);
};
renderOddRow(i, categoryLeft, categoryRight) {
return (
<View style={styles.oddRow} key={i}>
<CategoryImageItem
category={categoryLeft}
onShowRecipesForCategory={() =>
this.handleShowRecipesForCategory(categoryLeft)
}
/>
<CategoryImageItem
category={categoryRight}
onShowRecipesForCategory={() =>
this.handleShowRecipesForCategory(categoryRight)
}
/>
</View>
);
}
renderEvenRow(i, category) {
return (
<View style={styles.evenRow} key={i}>
<CategoryImageItem
category={category}
onShowRecipesForCategory={() =>
this.handleShowRecipesForCategory(category)
}
/>
</View>
);
}
render() {
return <View style={styles.container}>{this.loadViews()}</View>;
}
}
componentDidMount shouldn’t be async.
componentDidMount(){
this.getCategories()
}
getCategories = async () => {
console.log("loadCats");
let res = await fetch("http://192.168.1.4:4000/api/recipe_categories");
let categories = await res.json();
for (const category of categories) {
const imgUrl = await getS3Image(category.category_image);
category.imageUrl = imgUrl.split("?")[0];
}
console.log("returned");
this.setState({ categories });
console.log("set done...", categories);
}
If this doesn’t work can you add what you are getting from console.log("set done...", categories); and what you expected

How to update third party props value - Reactjs?

I'm using ant calendar which renders PanelBody(third party) component which has a prop call rowNum. Now I want to override this value to my custom value from my component:
import React from 'react';
import { Calendar } from 'antd';
import moment from 'moment';
import { Button } from '#material-ui/core';
import { getEventInfo, getQuest } from '../../../response/api';
import { EventInfo, Event } from '../../../response/type';
import { ConfigProvider } from 'antd';
import jaJP from 'antd/lib/locale/ja_JP';
interface State {
event_infos: EventInfo[];
events: Event[];
}
interface Props {
}
class EventCalendar extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
event_infos: [],
events: []
};
}
componentDidMount() {
getEventInfo().then((item) => {
this.setState({event_infos: item});
});
getQuest().then((item) => {
this.setState({events: item});
});
}
getCorrectFormatDate = date => {
return moment(date, 'YYYY-MM-DD').format('YYYY-MM-DD');
}
getListEventInfoDate = value => {
let listData = [];
let listEventInfo = this.state.event_infos;
listEventInfo.map((item) => {
const startTime = this.getCorrectFormatDate(item.start_at);
const calendarTime = this.getCorrectFormatDate(value);
if (startTime === calendarTime) {
listData.push(item);
}
return listData;
});
return listData || [];
}
getListEventDate = value => {
let listData = [];
let listEvent = this.state.events;
listEvent.map((item) => {
const startTime = this.getCorrectFormatDate(item.start_at);
const calendarTime = this.getCorrectFormatDate(value);
if (startTime === calendarTime) {
listData.push(item);
}
return listData;
});
return listData || [];
}
dateCellRender = value =>{
const listEventInfos = this.getListEventInfoDate(value);
const listEvents = this.getListEventDate(value)
return (
<ul className="events-calendar">
{listEventInfos.map((item, i) => <img key={i} src={item.image} alt="event_info" />)}
{listEvents.map((item, i) => <Button key={i} variant="outlined" color="primary" className="button-event">{item.name}</Button>)}
</ul>
)
}
render() {
moment.locale("ja");
return(
<ConfigProvider locale={jaJP}>
<Calendar
dateCellRender={this.dateCellRender}
className="hide-header"
validRange={[moment('2020-12-13', 'YYYY-MM-DD'), moment('2021-01-16', 'YYYY-MM-DD')]}
defaultValue={moment('2020-12-13')}
/>
</ConfigProvider>
)
}
};
export default EventCalendar;
This component render Calendar which renders PanelBody. I tried to create a duplicated component then overrided the prop value, imported it to EventCalendar, but it cannot update the props value as expected. Is it my approach wrong? Any suggestion for this issue?

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 Infinite scrolling function by online API

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;

React-Bootstrap - Pagination not showing

I'm using the react-bootstrap to paginate the result. It is rendering the div #content, but it is not showing nothing more. It is showing only a empty div with width, height and background color as I configured on the CSS file. I would like to display the pagination showing one house per page. The result of data from JSON is catched successfully. How can I solve the pagination issue? Thanks!
import React, { Component } from 'react'
import axios from 'axios'
import { Pagination } from 'react-bootstrap'
const URL_HOUSES = 'http://localhost:3001/houses';
class Casas extends Component {
constructor(props) {
super(props)
this.state = {
houses: []
}
this.handlePageChange = this.handlePageChange.bind(this)
}
getNumPages(currentPage) {
{ this.handlePageChange }
this.setState({
per_page: this.props.results ,
currentPage: currentPage + 1 ,
previousPage: currentPage - 1
});
}
handlePageChange(page, evt) {
const currentPage = this.state.currentPage || 1;
const numPages = this.getNumPages();
const pageLinks = [];
if (currentPage > 1) {
if (currentPage > 2) {
pageLinks.push(1);
pageLinks.push(' ');
}
pageLinks.push(currentPage - 1);
pageLinks.push(' ');
}
for (let i = 1; i <= numPages; i++) {
const page = i;
pageLinks.push(page);
}
if (currentPage < numPages) {
pageLinks.push(' ');
pageLinks.push(currentPage + 1);
if (currentPage < numPages - 1) {
pageLinks.push(' ');
pageLinks.push(numPages);
}
}
this.setState({ currentPage: currentPage + 1 } );
this.setState({ previousPage: currentPage - 1 } );
}
componentDidMount() {
axios.get(URL_HOUSES)
.then(res => {
this.setState({ houses: res.data })
})
}
render() {
const per_page = "1";
const paginationData = this.state.houses
let numPages = Math.ceil(paginationData.length / per_page);
if (paginationData.length % per_page > 0) {
numPages++;
}
return (
<div>
{this.state.houses.map(item =>
<div>
<h2>{item.name}</h2>
<p>{item.description}</p>
<ul>
{
item.photos.map(photo => <li>{photo}</li>)
}
</ul>
</div>
)}
<Pagination id="content" className="users-pagination pull-right"
bsSize="medium"
first last next prev boundaryLinks items={numPages}
activePage={ this.state.currentPage } onSelect={ this.handlePageChange
} />
</div>
)
}
}
export default Houses;
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Pagination from "react-js-pagination";
require("bootstrap/less/bootstrap.less");
class App extends Component {
constructor(props) {
super(props);
this.state = {
activePage: 1,
itemPerPage: 3,
productList: [],
duplicateProductList: []
};
}
componentDidMount() {
let d = '';
$.get("YOUR API", function (data) {
d = data;
this.setState({
projectList: d,
duplicateProductList: d
});
}.bind(this));
}
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
render() {
const { projectList, activePage, itemPerPage } = this.state;
const indexOfLastTodo = activePage * itemPerPage;
const indexOfFirstTodo = indexOfLastTodo - itemPerPage;
const renderedProjects = projectList.slice(indexOfFirstTodo, indexOfLastTodo);
return (
<div>
<div>
YOUR LIST
</div>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemPerPage}
totalItemsCount={this.state.duplicateProductList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Can you follow this link https://www.npmjs.com/package/react-js-pagination

Categories

Resources