This question already has answers here:
How to sort array of objects based on a boolean property?
(2 answers)
Javascript sort array of objects by a boolean property
(14 answers)
Closed 3 years ago.
I have a JSON file:
[
{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
}
]
What I would like to achieve is for the JSON with "availability : true" to automatically appear above the "availability : false". So that the pink appears above the blue like this:
This is my code so far which simply displays them in the same order as the JSON file:
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
posts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="color">{post.color}</p>
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
I am unsure of how to achieve this and have struggled to find any suggestions so far so any help would be great.
var data = [{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
},
{
"id": 3,
"color": "Pink",
"availability": true
},
{
"id": 4,
"color": "Pink",
"availability": false
}, {
"id": 5,
"color": "Pink",
"availability": true
}
]
//unordered
data.sort(val => val.availability ? -1 : 1) //simple one line sort function for boolean
console.log(data);
// ordered based on your default array order
data = [{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
},
{
"id": 3,
"color": "Pink",
"availability": true
},
{
"id": 4,
"color": "Pink",
"availability": false
}, {
"id": 5,
"color": "Pink",
"availability": true
}
]
data.sort((a, b) => b.availability - a.availability);
console.log(data);
You can filter the two sets, create a new array and then render it.
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
const orderedPosts = [...posts.filter((post) => post.availability), ...posts.filter((post) => !post.availability)]
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="color">{post.color}</p>
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
Related
I am developing an application using react class components. Below is my component. Data is fetched asynchronously from an API endpoint.
import React, { Component } from 'react';
...etc etc
import { doGetCompilation } from '../controllers/compilationController';
class Forms extends Component {
constructor() {
super();
this.state = {
step: 1,
};
this.getCompilation = this.getCompilation.bind(this);
}
state = {
step: 1,
compilation: []
};
componentDidMount() {
this.setState({ isLoading: true });
this.getCompilation();
}
async getCompilation() {
this.setState({ isLoading: true });
try {
let data = await doGetCompilation('ceb9baad7baefeb4d9d94cae8082452f53563baf');
this.setState({ isLoading: false, compilation: data.data });
} catch (error) {
console.log(error);
}
}
render() {
const {
step,
compilation,
isLoading,
} = this.state;
console.log('compilation', compilation);
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (compilation && Array.isArray(compilation.steps) &&
compilation.steps.map((step, index) => (
<Paper variant="outlined" sx={{ my: { xs: 3, md: 6 }, p: { xs: 2, md: 3 } }}>
<Stepper
steps={step}
activeStep={0}
/>
<Form/>
</Paper >
)))
}
</>
)
}
}
export default Forms;
I'm getting an error while mapping over my compilation object. This object is undefined initially and then it is populated on componentDidMount. But this is not something react seem to like. In fact no matter what i can not render my component. Can you suggest a solution or an interesting article to read about this behaviour.
The error i get is :
Forms.jsx:81 Uncaught TypeError: Cannot read properties of undefined (reading 'map')
object:
[
{
"id": 3,
"token": "ceb9baad7baefeb4d9d94cae8082452f53563baf",
"form": {
"id": 17,
"name": "aziende"
},
"steps": [
{
"id": 4,
"name": "STEP 2 - Informazioni lavorative",
"weight": null,
"questions": []
},
{
"id": 5,
"name": "STEP 2 - Informazioni personali",
"weight": null,
"questions": []
},
{
"id": 3,
"name": "STEP 1 - Informazioni preliminari",
"weight": null,
"questions": []
}
]
}
]
try to put the compilation: [] inside the state in the constructor
I'm developing an App in ReactJS, and I have a page where I want to show two select, one dependent on the other.
I'm using react-select and #material-ui.
In dates:
[
{
"id": 1,
"name": "202001"
},
{
"id": 2,
"name": "202002"
},
{
"id": 3,
"name": "202003"
},
{
"id": 4,
"name": "202004"
},
{
"id": 5,
"name": "202005"
},
{
"id": 6,
"name": "202006"
},
{
"id": 7,
"name": "202007"
}
]
I have a list of dates that are available to select.
import React, { useState, useEffect } from "react";
import Select from "react-select";
...
const App = () => {
...
const DateA = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const DateB = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const [dateA, setDateA] = React.useState(null);
const [dateB, setDateB] = React.useState(null);
function handleChangeDateA(value) {
setDateA(value);
}
function handleChangeDateB(value) {
setDateB(value);
}
return (
<div className="App">
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateA"
TextFieldProps={{
label: "DateA",
InputLabelProps: {
htmlFor: "DateA",
shrink: true,
},
placeholder: "DateA...",
}}
options={DateA}
components={components}
value={dateA}
onChange={handleChangeDateA}
/>
</div>
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateB"
TextFieldProps={{
label: "DateB",
InputLabelProps: {
htmlFor: "DateB",
shrink: true,
},
placeholder: "DateB...",
}}
options={DateB}
components={components}
value={dateB}
onChange={handleChangeDateB}
/>
</div>
</div>
);
};
export default App;
The idea is that the DateB select take dates greater than the ones selected in the DateA select.
How can I do this, suggestions?
Try this
useEffect(() => {
if (condition) {
setDateB(value)
}
}, [DateA])
I am making a guide me section. What this guide me section does - displays an array of processes. Within each process is an array of steps, within each step is an array of options. The user selects an option from one of the steps, it takes them to the next correlating step. If the user selects the option on step 2, it could take them to step 3 or back to step 1. It depends on the id.
With all that said I'm having issues with my Process mutating on me. I'm using React Context as a global state. When a user selects an option, I'm grabbing that id, then filtering the designated object by that id. So I should only be left is that processes with that specific step. What's happening is my initial global state is mutating somehow. I'm missing something here as I'm new to React.
P.s - I'm not using any services at this moment, so I just copied some JSON over to my initial state in context.js
context.js
import React, { Component } from 'react'
// import axios from 'axios'
const Context = React.createContext()
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_PROCESS':
return {
...state,
guides: action.payload
}
default:
return state
}
}
export class Provider extends Component {
state = {
guides: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585x",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
initialGuide: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
dispatch: action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
Guides.js
import React, { Component } from 'react'
import { Consumer } from '../../context'
import Process from './Process'
class Guides extends Component {
constructor (props) {
super(props)
this.state = {
contextValue: [],
searchData: props.location.data
}
}
render () {
console.log(this.props.location.data, this.state, 'logging state and props on guides')
// this.state.searchData = this.props.location.data
return (
<Consumer>
{value => {
return (
<React.Fragment>
<div className="content-wrapper">
<h1>Guide Me</h1>
<div className="ms-Grid times--max-width" dir="ltr">
<div className="ms-Grid-row">
<div className="profile--wrapper ms-Grid-col ms-sm12 ms-md12 ms-lg5">
{value.guides.map(item => {
return <Guide key={item.id} guide={item} processValue={value.guides} initialGuide={value.initialGuide}/>
})}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}}
</Consumer>
)
}
}
export default Guides
Process.js
import React, { Component } from 'react'
import GuideSteps from './Guide-Steps'
import { Consumer } from '../../context'
class Process extends Component {
constructor(props) {
super(props)
this.state = {
processName: this.props.guide.processName,
process: this.props.guide,
steps: this.props.guide.steps,
selectedIndex: 0,
selectedStep: '',
processValue: this.props.processValue,
initialGuide: this.props.initialGuide
}
this.displayStep = this.displayStep.bind(this)
}
displayStep = (res, dispatch) => {
this.setState({ selectedStep: res })
}
render() {
const { steps, selectedIndex, process, processName, processValue, initialGuide } = this.state
return (
<Consumer>
{value => {
return (
<div>
<h2 className="profile--sub-header--bold">{processName}</h2>
<GuideSteps
key={this.props.guide.steps[selectedIndex].id}
selectedStep={this.props.guide.steps[selectedIndex]}
stepValue={this.displayStep}
process={process}
processValue={processValue}
initialGuide={initialGuide}
/>
</div>
)
}}
</Consumer>
)
}
}
export default Process
Guide-Steps.js
import React, { Component } from 'react'
import { ChoiceGroup } from 'office-ui-fabric-react/lib/ChoiceGroup'
import { Consumer } from '../../context'
class GuideSteps extends Component {
constructor(props) {
super(props);
this.state = {
process: [],
selectedStep: this.props.selectedStep,
dispatch: '',
processValue: this.props.processValue,
initialGuide: ''
}
this._onChange = this._onChange.bind(this)
}
_onChange = (ev, option) => {
// this.props.stepValue(option.value.nextStep)
const { dispatch , initialGuide } = this.state
let optionArray = option.value.nextStep.split(',')
let processArray = this.state.process.filter(item => {
return item.id === optionArray[0]
})
let stepArray = processArray[0].steps.filter(item => {
return item.id === optionArray[1]
})
console.log(stepArray, processArray, this.state.process, 'logging step array before setting')
processArray[0].steps = stepArray
console.log(stepArray, processArray, this.state.process, 'logging step array after setting')
dispatch({
type: 'SEARCH_PROCESS',
payload: processArray
})
}
render() {
let options = []
{
this.props.selectedStep.options.map(item => {
return options.push({
key: item.text,
text: item.text,
value: item
})
})
}
return (
<Consumer>
{value => {
const { dispatch, guides, initialGuide } = value
this.state.dispatch = dispatch
console.log(value, 'logging initial guide in render')
this.state.process = initialGuide
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
options={options}
onChange={this._onChange}
/>
</div>
)
}}
</Consumer>
)
}
}
export default GuideSteps
On change in GuideSteps is where I'm doing the logic for filtering and setting up my new object.
EDIT
This fixed the issue but I think it's too expensive. How would I go about solving this issue without having to reparse the array.
update: (ev, option) => {
const { initialGuide } = this.state
if (option.value.nextStep !== null && option.value.nextStep !== '') {
//split string
const optionArray = option.value.nextStep.split(',')
//filter process array
const processArray = initialGuide.filter(process => {
return process.id === optionArray[0]
})
//filter step array
const stepArray = processArray[0].steps.filter(
item => item.id === optionArray[1]
)
if(stepArray.length > 0 && stepArray !== null) {
//get a copy of the process array so original is not mutated by the steps
let stringC = JSON.stringify(processArray)
let stringD = JSON.parse(stringC)
stringD[0].steps = stepArray
//issue might be here visually where setting the state happens quickly, therefore radio button visual does not display in time.
setTimeout(() => {
this.setState({ guides: stringD })
}, 200)
}
}
},
this.state.process = initialGuide
let processArray = this.state.process.filter...
processArray[0].steps = stepArray
So it looks like you're mutating initialGuide via reference.
I'm kind of lost to access some info in my static data. Here's the data :
{
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
}
Then I get the first info like that :
this.setState({
currentLabel: this.state.labels["info1"]
})
This is why I want and then I want to display info in a component and it's working until I try to get tags information. I tried a .map() but without success and error.
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
<Text>How do I get "tags" information</Text>
</View>
Is it possible to access these objects in the array "tags" ?
yes you can call tags as follows infoDetail.tags and do map on it
render(){
const tagItems = infoDetail && infoDetail.tags.map((item, index) => {
return <Text key={index}>{item.name}</Text>
});
return(
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{tagItems}
</View>
)
}
Here is a full working code. Since your labels state property is an object, you need to map it somehow. I've chosen Object.values here. You can use Object.keys or even Object.entries according to your needs.
I've used a separate Info component and passed the values to it, then render there. In this component, we are again mapping the tags, then rendering the list.
class App extends React.Component {
state = {
labels: {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter",
},
{
name: "#myspace",
},
],
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram",
},
{
name: "#facebook",
},
],
},
},
}
render() {
const { labels } = this.state;
return (
<div>
{
Object.values( labels ).map( value =>
<Info label={value} key={value.label} /> )
}
</div>
);
}
}
const Info = ( props ) => {
const { title, text, tags } = props.label;
const tagList = tags.map( tag => <p key={tag.name}>{tag.name}</p> );
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update
If your data is totally static then #Xavi A.'s method is a good option. I don't know how is your list but I provide a simple code including something like you want here.
const labels = {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter"
},
{
name: "#myspace"
}
]
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram"
},
{
name: "#facebook"
}
]
}
};
class App extends React.Component {
state = {
currentLabel: Object.keys(labels)[0]
};
handleInfoChange = info => this.setState({ currentLabel: info });
renderList = () => (
<ul>
{Object.keys(labels).map(info => (
<Item key={info} info={info} onClick={this.handleInfoChange} />
))}
</ul>
);
render() {
const { currentLabel } = this.state;
return (
<div>
{this.renderList()}
<Info currentLabel={currentLabel} />
</div>
);
}
}
const Item = props => {
const { info, onClick } = props;
const handleClick = () => onClick(info);
return <li onClick={handleClick}>{info}</li>;
};
const Info = props => {
const { currentLabel } = props;
const { title, text, tags } = labels[currentLabel];
const tagList = tags.map(tag => <p key={tag.name}>{tag.name}</p>);
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Probably something like this.
<Text>{infoDetail.tags.map(tag => {/*render */})}</Text>
You can try Object.keys() and Array.prototype.reduce() to get your favorite data:
const data = {
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
};
const tags = Object.keys(data).reduce((result, key) => {
return result.concat(data[key].tags);
}, [])
console.log(tags);
/* tags = [
{
"name": "#twitter"
},
{
"name": "#myspace"
},
{
"name": "#instagram"
},
{
"name": "#facebook"
}
] */
No need to save all the static data in your state, you can keep your state cleaner by just saving the selected label:
onLabelSelect = label => {
//label will be "info1" for example
this.setState({
currentLabel: label
})
}
Then in your render:
render(){
//get infoDetail from staticData
const infoDetail = staticData[this.state.currentLabel]
return (
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
</View>
)
}
Note about the map. This:
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
is a shorter version of:
{infoDetail.tags.map( item => {
return <Text>item.name</Text>
})}
I've tried to get a minimal example of infinite scroll.
So I have this:
var React = require('react-native');
var {
StyleSheet,
View,
Image,
ListView,
} = React;
var data = [
{
"id": 1,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 2,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 3,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 4,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 5,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 6,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
}
];
var InfiniteScreen = React.createClass({
getInitialState: function () {
return {
isLoadingTail: false,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
},
componentDidMount: function () {
this.setState({
dataSource: this.getDataSource(data)
});
},
renderRow: function (item) {
return (
<View>
<Image style={{width: 80, height: 80}} source={{uri: 'http:' + item.profile_picture.href}}/>
</View>
);
},
onEndReached: function () {
console.log('onEndReached', this.state.isLoadingTail);
if (this.state.isLoadingTail) {
// We're already fetching
return;
}
this.setState({
isLoadingTail: true
});
this.setState({
isLoadingTail: false,
dataSource: this.getDataSource(data)
});
},
getDataSource: function (users):ListView.DataSource {
return this.state.dataSource.cloneWithRows(users);
},
render: function () {
return (
<View>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
onEndReached={this.onEndReached}
/>
</View>);
}
});
If I scroll to the very bottom, onEndReached() is fired, but the new data doesn't appear. Any ideas?
You always clone your data source with the same data, so nothing new appears. Here is a working example (add new data via concat):
var React = require('react-native');
var {
StyleSheet,
View,
Image,
ListView,
} = React;
var data = [
{
"id": 1,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 2,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 3,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 4,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 5,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
},
{
"id": 6,
"profile_picture": {
"href": "//like1.r.worldssl.net/ui_big/1305634.jpg"
}
}
];
var InfiniteScreen = React.createClass({
getInitialState: function () {
return {
isLoadingTail: false,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
},
componentDidMount: function () {
this._data = [];
this.setState({
dataSource: this.getDataSource(data)
});
},
renderRow: function (item) {
return (
<View>
<Image style={{width: 80, height: 80}} source={{uri: 'http:' + item.profile_picture.href}}/>
</View>
);
},
onEndReached: function () {
console.log('onEndReached', this.state.isLoadingTail);
if (this.state.isLoadingTail) {
// We're already fetching
return;
}
this.setState({
isLoadingTail: true
});
this.setState({
isLoadingTail: false,
dataSource: this.getDataSource(data)
});
},
getDataSource: function (users):ListView.DataSource {
this._data = this._data.concat(users);
return this.state.dataSource.cloneWithRows(this._data);
},
render: function () {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
onEndReached={this.onEndReached}
/>
</View>);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
});
https://facebook.github.io/react/docs/component-api.html#setstate
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
so the problems is here
this.setState({
isLoadingTail: true
});
this.setState({
isLoadingTail: false,
dataSource: this.getDataSource(data)
});
I've been looking for this solution for very long time. Finally I came up with this light weight library :-
https://www.npmjs.com/package/rn-infinite-scroll
import InfiniteListView from 'rn-infinite-scroll';
<InfiniteListView
data ={this.state.items}
renderRow={this.renderRow}
canLoad={this.canLoad()}
isLoading={this.state.isLoading}
onLoad={this.onLoad}
/>
ListView is currently Deprecated. You will have to use FlatList or SectionList. You can find an example of infinite scroll using FlatList in this answer for another question. https://stackoverflow.com/a/47710224/7477198.