Pull to Refresh and setState not working - React Native - javascript

React Native App
I am implementing Pull to refresh in my Flatlist prop "onRefresh"
onRefresh called the function "refreshcontrol()" see code below.
I need to change my state "refreshing" to true before I fetch from my API. But it throws the maximum update error.
export default class NotificationScreen extends Component {
constructor(props) {
super(props)
this.state = {
refreshing: false
}
}
.
.
.
.
.
refreshControl() {
const { refreshing } = this.state;
this.setState({ refreshing : true )}. // throws maximum update error
return (
<RefreshControl
refreshing={refreshing}
onRefresh={fetchNotifications.bind(this)} //fetch from API only when refreshing is true
colors={['#2B70AD']}
/>
);
};
}
How else can I set my state to "refreshing : true" ??? Help please!!!!
This is how it was fixed. Solution:
refresh = async() => {
this.setState({refreshing : true})
try {
const notifications = await fetchNotifications();
this.setState({
notifications,
error: null,
refreshing: false
});
} catch (error) {
this.setState({
notifications: [],
error,
refreshing: false
});
}
}
refreshControl() {
const { refreshing } = this.state;
return (
<RefreshControl
refreshing={refreshing}
onRefresh={this.refresh}
colors={['#2B70AD']}
/>
);
};

refreshFlatlist = () => {
this.setState(
{
refresh: true,
},
() => this.getTodosHandler()
);
this.setState({
refresh: false,
});
};
This is how I refresh the default state of course is false. the todosHandler always holds the current todos. Its a call to a SQLite DB stored on the phone locally.
Now the flatlist RefreshComponent I used:
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refresh}
onRefresh={this.refreshFlatlist}
/>
}
extraData={this.state.refresh}
data={this.state.toDoArray}
keyExtractor={(item, index) => item.id.toString()}
renderItem={({ item }) => ( ...
Look into it maybe it will help you - this works for me like a charm ;)

This should work perfectly. I think there is a typo in your code. You are using try instead of true. I think that might be the cause of the error.

This might not be the issue but I'll put it here for others (like me) trying to debug:
Make sure that your list is not put inside a ScrollView!

Related

component in react native gets updated itself in RefreshControl

So I am trying to implement a pull to refresh feature in my app. And to say I have successfully implemented this in my Android platform where (in simulator) If I pull down (by mouse) then the refreshing indicator will stay visible until I leave the mouse click and the component will not update until the mouse click as well. It will update the component view when I leave the mouse click and refreshing indicator will be hidden in 2 sec. The similar thing is not exactly working as expected in ios, so when I pull down the screen, the component somehow updates even when I haven't left the mouse click. I have given it a googling but probably haven't been able to find the right search keyword.
Below is code snippet of mine. Thanks in advance.
render() {
const { loadingCart } = this.props;
return (
<View style={styles.container}>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.contentContainer}
contentInsetAdjustmentBehavior="automatic"
horizontal={false}
refreshControl={this._renderRefreshingControl()}
>
{this._renderProduct()}
{loadingCart && this._renderLoadingCart()}
</ScrollView>
{this._renderCartButton()}
{this._renderAddToCartPopover()}
</View>
);
}
_renderRefreshingControl = () => {
const { refreshing } = this.state;
return (
<RefreshControl refreshing={refreshing} onRefresh={this._handleRefreshingControlVisibility} />
);
};
_handleRefreshingControlVisibility = async () => {
const { fetchProductByCode, navigation } = this.props;
this.setState({
refreshing: true,
});
const resultAction = await fetchProductByCode('38186');
if (resultAction.type === PRODUCT_FETCH_SUCCESS || resultAction.type === PRODUCT_FETCH_FAILURE) {
this.setState({
refreshing: false,
});
};
};
I hope I have been able to clarify my question :-)
Try this out:
_handleRefreshingControlVisibility = () => {
this.setState({ refreshing: true });
setTimeout(function() {
// here do what you want
}, 1500);
};
So the RefreshControl was working as expected, I had a loading flag in my render method, somehow this would be set to false when then refreshing is set true in this.setState({ refreshing: true, })
eventually removing the loading flag solved my issue.

Rendering Content Conditionally in React JS Based on the state

I have a page that renders questions that have been posted. I want to create a button that displays only answered questions based on the state = {isAnswered: true}.
Is the state isAnswered is true then onClick will display answered questions only where isAnswered is set to true in the object.
How can I used this Filter button to conditionally render these based on their state.
Should the function be stored as constant called in the render function or before this?
this.state.posts is an array of these objects on the back end:
Here is what I have attempted.
class Posts extends Component {
state = {
posts: []
}
render () {
let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>;
let {isAnswered} = this.state;
const renderAuthButton = () => {
if (isAnswered === true) {
if ( !this.state.error ) {
posts = this.state.posts.map( (post) => {
return (
<Post
key={post.key}
id={post.key}
title={post.title}
type={post.type}
body={post.body}
answer={post.answer}
onChange={(value, id) => this.postAnswerHandler(value,id)}
clicked={(body) => this.displayAnswerHandler(body)}
/>
);
} );
}
}
}
}
return (
<button onClick={renderAuthButton()}>Filter</button>
{posts}
)
You are misinterpreting your data structure. this.state has a property this.state.posts which is an array. Each element in the array is an object with multiple properties including isAnswered.
When you do this:
let {isAnswered} = this.state;
You are looking for a property this.state.isAnswered which does not exist. There is no top-level isAnswered property. It is something that exists within each post object and is different for every post. So you need to be looking at isAnswered inside of your loop.
There's honestly a lot that's weird and backwards here. Don't create a callback inside of render()! Don't return JSX from a callback!
Here's my attempt to clean it up. I am adding a property to this.state which tells us whether or not to filter the posts. Clicking the button changes this.state.isFiltered. The render function renders appropriately based on the current state.
class Posts extends Component {
state = {
posts: [],
isFiltered: false,
isError: false
};
async componentDidMount() {
// do your API fetch and set the state for `posts` and `isError`
try {
const fetchedPosts = someApiFunction();
this.setState({
posts: fetchedPosts
});
} catch (error) {
this.setState({
isError: true
});
}
}
onClickFilter = () => {
// toggles filter on and off
this.setState((prevState) => ({
isFiltered: !prevState.isFiltered
}));
};
render() {
if (this.state.isError) {
return <p style={{ textAlign: "center" }}>Something went wrong!</p>;
}
// show only answered posts if isFiltered is true, or all posts if false
const visiblePosts = this.state.isFiltered
? this.state.posts.filter((post) => post.isAnswered)
: this.state.posts;
return (
<>
<button onClick={this.onClickFilter}>Filter</button>
{visiblePosts.map((post) => {
return (
<Post
key={post.key}
id={post.key}
title={post.title}
type={post.type}
body={post.body}
answer={post.answer}
onChange={(value, id) => this.postAnswerHandler(value, id)}
clicked={(body) => this.displayAnswerHandler(body)}
/>
);
})}
</>
);
}
}

Why I cant set value to my states? REAECT-NATIVE

im new in react-native, im passing data by navigation to my edit_note screen, once i received i set it to my states, but it doesnt work, if i print them, it shows their values, but setting to my states doesnt work, heres the code:
heres the Notes class, in the navigation function im passing the datas, data and note_number to Edit_note
render() {
return (
<>
<View style = {this.styles.View}>
<FlatList data = {this.props.data} renderItem = {({item}) => (<TouchableOpacity onPress = {() => this.props.navigation.navigate("Edit_note", {data: this.props.data, note_number: item.note_number})}><Text style = {this.styles.Text}>{item.title}</Text></TouchableOpacity>)} keyExtractor = {(item) => item.note_number.toString()}></FlatList>
</View>
</>
);
}
in Edit_note im receiving it like this:
class Edit_note extends Component {
constructor() {
super();
this.state = {
array_notes: [],
note_number: "",
}
}
componentDidMount() {
const {params} = this.props.navigation.state;
let x = params.note_number;
this.setState({note_number: x});
console.log(this.state.note_number);
}
render() {
return (
<Text></Text>
);
}
}
if i print x, it will print the note_number, but setting it into note_number, and print it, it doesnt show anything, why?
You actually set it but your console.log() fires before the change.
After a state changes component rerenders so you can try printing it on screen like;
render() {
return (
<Text>{this.state.note_number}</Text>
);
}
setState is asynchronous that means that the state indeed changes but the console.log right after the setState is not showing the change.
You can learn more about it here.

Call another code while array.map() is running

I have the following react code:
{myArray.map(arr => {
return ( <MyComponent title={arr.ttile} /> )
})}
I would like to call a Loading component while the map() is not completely finished. Is it possible to do that? If yes, how would I do that?
If you are getting your data from an API, you might want to render the data as usual, but you can get the data in the componentDidMount hook instead, and e.g. keep an additional piece of state isLoading which you can use in the render method to decide if you should show a loading component.
Example
function getBooks() {
return new Promise(resolve => {
setTimeout(() => resolve([{ title: "foo" }, { title: "bar" }]), 1000);
});
}
function MyComponent(props) {
return <div> {props.title} </div>;
}
class App extends React.Component {
state = { books: [], isLoading: true };
componentDidMount() {
getBooks().then(books => {
this.setState({ books, isLoading: false });
});
}
render() {
const { isLoading, books } = this.state;
if (isLoading) {
return <div> Loading... </div>;
}
return (
<div>
{this.state.books.map(book => <MyComponent title={book.title} />)}
</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>
If you want to actually be able to see the components being loaded behind/under the loading indicator, then it would be more challenging and would probably need more work than this proposed solution. But if you just want a loading indicator to show while the .map() prototype function is working, I believe this would do the trick:
constructor(props) {
super(props);
this.state = { loadingIndicator : null };
}
getArrayOfMyComponents() {
return myArray.map((arr, index) => {
if (index === 0) {
const loadingIndicator = <Loading/>;
this.setState({ loadingIndicator : loadingIndicator });
} else if (index === myArray.length - 1) {
this.setState({ loadingIndicator : null });
}
return ( <MyComponent title={arr.title} /> );
});
}
render() {
const arrayOfMyComponents = this.getArrayOfMyComponents();
return (
<div>
{this.state.loadingIndicator}
{arrayOfMyComponents}
</div>
);
}
Array.prototype.map() is really just a fancier version of Array.prototype.forEach(). So we can leverage that fact to launch the display of the loading indicator on the first iteration and remove it on the last.
you can have a boolean in a state, and just before you start array map put boolean true and run another code o component render, and then when array maps end you put that state to false, for redux im using state fetch start, fetching, fetched, and then you can take the control of situation

Returning options from mapping over values in react

Hey everyone so I am making a get request to a google API and pulling in some data. Initially, my state value is just an empty object and from the ajax request I am expecting the state values to be filled with setState.
Then, in another method I am taking that state data and mapping over the items and returning an option for each element within that array. The weird thing is that right before I start returning the values that I am mapping over I am console.loging the values and they are exactly the values that I want. However, when I return an option with that value inside there is still nothing inside my select.
Can anyone please elaborate what I could be possibly doing incorrectly?
constructor() {
super();
this.state = {
};
componentDidMount() {
let URL = is the url with my api key (it works)
axios.get(URL)
.then((data) => {
console.log("data" + data);
this.setState({
googleFonts: data
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
renderFonts() {
let data = this.state.googleFonts.data.items;
return data.map((font, index) => {
console.log(font.family);
return (
<ul>
<li>{font.family}</li>
</ul>
)
})
}
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select" >
{setTimeout(this.renderFonts, 100)}
</FormControl>
Could you try something like this:
//after `render()`
const data = this.state.googleFonts.data.items
return (
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select"
>
{data && data.map((font, index) =>
<option>{font.family}</option>
)}
</FormControl>
I believe that's how React-Bootstrap is expecting it to be written, and it's a little less code. Using the logical && operator allows it to populate the dropdown after the state loads.
There are a few things wrong here.
First: {setTimeout(this.renderFonts, 100)} returns timeoutID is a positive integer value which identifies the timer created by the call to setTimeout() and hence although renderFonts is executed nothing is returned from it to be rendered
Second: componentDidMount is called after render and you have your API request in componentDidMount, so delaying the value to be rendered by using a setTimeout is a horrible idea, since you are never sure as to when you would get a response from the API, for a slow network it could take a really long time and hence even after a timeout of 100ms, you may still not have the data and so this.state.googleFonts.data.items might still be undefined or the response may come within 10ms in which case you are unnecessarily delaying the render.
A better way to solve this is to have provide a check for the presence of data.
You could do it like
constructor() {
super();
this.state = {
};
componentDidMount() {
let URL = is the url with my api key (it works)
axios.get(URL)
.then((data) => {
console.log("data" + data);
this.setState({
googleFonts: data
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
renderFonts() {
let data = this.state.googleFonts? this.state.googleFonts.data.items: [];
return data.map((font, index) => {
console.log(font.family);
return (
<ul>
<li>{font.family}</li>
</ul>
)
})
}
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select" >
{this.renderFonts()}
</FormControl>
This should help you. It's more to do with how you have structured your code and callback. Take a look at this sample -
import React from 'react';
import axios from 'axios'
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'there',
title: null
};
}
componentWillMount() {
let URL = 'https://fakerestapi.azurewebsites.net/api/Books'
axios({
method: 'get',
url: URL,
responseType: 'json'
})
.then((data) => {
console.log(data.data);
this.setState({
title: data.data[0].Title
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
render() {
return (
<div className="component-app">
<h1>Hello {this.state.name}! {'\u2728'}</h1>
<h2>{this.state.title}</h2>
</div>
);
}
}
export default Hello;
I have coded it here. Have a look
Instead of componentDidMount you can use componentWillMount. Also you would be seeing errors in console with your code somthing like React - setState() on unmounted component. If you fix that you should be able to get your code working.

Categories