Why use this.setState will cause Maximum update depth exceeded? - react-native

In my class component i fetch API from react-redux to render my view. So i set a loading flag like this:
render() {
const theData = this.props.theData;
if(this.props.loading) {
return (
// Show loading view
);
}
console.log('I am test message');
return (
// This is real view want to show
);
}
If i run this i can see my log show I am test message just one time, because i fetch my API succeed and re-render view then pass if(this.props.loading).
Now i want to use theData with this.setState, so i try it like this:
render() {
const theData = this.props.theData;
if(this.props.loading) {
return (
// Show loading view
);
}
console.log('I am test message');
// Here is what i try
this.setState({ theData });
return (
// This is real view want to show
);
}
I think this.setState will run just one time too. But when i run it i see the error Maximum update depth exceeded
Why ? I have no idea with the situation. And if i want to use this.setState with avoid this error which place should be the better way ?
Any help would be appreciated. Thanks in advance.

this.setState({ theData }); , calling this method will cause the component to be rendered again and that is why you are getting the error. Set the state with theData in the constructor or the API call. Also in constructor , do not use the setState method , directly assign it . Ex : this.state = {}

Related

Hold back react native UI with async until data from Firebase is fully loaded

My db is loading fine from Firebase.
How do I write async function correctly to make sure nothing renders until setDATA is set with the data from the db
useEffect(() => {
const db = getDatabase();
return onValue(ref(db, "/beaches"), (querySnapShot) => {
setDATA(querySnapShot.val() || {});
});}, []);
The simplest way is going to be to wrap a conditional around the JSX that you don't want to render, and check whether the data exists.
If we say that your data is getting assigned to a variabl called data, this would look like
return (
<>
{!!data && (
<your jsx here>
)}
</>
)

Setting data returned by useQuery as state

I have been using client.query to get data from my database and setting states using useState.
Here's an example:
const [videos, setVideos] = useState([]);
client.query({ query: GET_VIDEOS })
.then(response => {
setVideos(response.data.videos);
})
However, this does not load 100% of the time. Usually, it doesn't load when I load the app for the first time in a while. I typically have to reboot in these situations.
This issue makes me want to look into useQuery instead of client.query.
However, the examples in the documentation only show how we can use useQuery to make direct changes in the components.
Example:
function Dogs({ onDogSelected }) {
const { loading, error, data } = useQuery(GET_DOGS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<select name="dog" onChange={onDogSelected}>
{data.dogs.map(dog => (
<option key={dog.id} value={dog.breed}>
{dog.breed}
</option>
))}
</select>
);
}
However, I don't want to make changes to the components right now. I would much rather query the data and then set the queried data as a state, using useState.
How would I best accomplish this? One idea that I had was to create a component that returns null but queries the data. It would look something like this:
function Videos() {
const { loading, error, data } = useQuery(GET_VIDEOS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
setVideos(data);
return null;
}
I was curious to hear what are best practices for such situations.
react-apollo's useQuery has an onCompleted as one of its options, so you can use it like
const { loading, error } = useQuery(GET_VIDEOS, {
onCompleted: (data) => setVideos(data)
});
the best practise would be to use the data directly, without setting the state.
you have only showed the calling of setState, somewhere in your component the video state variable is used right?
pass the fetched data directly there. you need not call "setVideos" to trigger the change. whenever the query result is changed the UI will be changed accordingly.
There is useLazyQuery if your use case is not to fetch the data upfront.

Populate UI once data finishes loading

I am fetching data from a server, and I would like to do this every time the user enters the screen. The problem is, I start getting the data when the screen function is called, and apparently there isn't enough time to fetch the data before the screen is populated. What I am trying now is something like:
function MyScreen() {
model.refreshData();
const data = model.getData();
return (<View><Text>{data}</Text></View>);
}
This does not work, because, again, data has not been refreshed by the time that we do the "getData". I can't use a .then, because then I won't be able to return the (<View><Text>{data}</Text></View>) part. Is there a way that I can force the screen to update once model.refreshData() has finished?
Use reactive state to change the return value of MyScreen when the data is done loading. The following is pseudocode:
function MyScreen() {
const [screenData, setScreenData] = useState(null);
model.refreshData();
setScreenData(model.getData());
// Component will render nothing until data is finished loading
if (!screenData) {
return null;
}
// Once data is loaded your full component will render
return (<View><Text>{screenData}</Text></View>);
}
If getData() is a promise then instead of
setScreenData(model.getData());
do
model.getData().then((data) => setScreenData(data));

Cant return an element in react-native

In my new react native app, I can't write the following code.
renderContent = () => {
let content = null;
content = <View><Text>HI</Text></View>;
return content;
}
I use this inside the render() like this.
render() {
{this.renderContent}
}
It gives me the following error.
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
my RN version is 59. I used this pattern successfully in version 54. What am I doing wrong here?
You have to follow this format:
render(){
return this.renderContent()
}
{something} is for jsx. For example for this:
return(
<View>{something}</View>
)

Why I can't read a state in react native?

I have the following code:
export default class Testing extends Component {
state = ({
data: []
});
componentDidMount() {
this.setState({
data: this.props.values
});
console.log(this.state.posts); //prints empty but if I do
console.log(this.props.values); //prints the array correctly
}
Where is the error since I can print the props not the state?
Thanks
You're not storing anything in this.state.posts. Your initial state only contains data.
Also when you construct your initial state you should do it like this:
state = {
data: []
}
You do not need the ( ) around it.
If you are wanting to print a value from state as soon as you have stored it you must use the callback functionality of state. This is due to the fact that setState is asynchronous and takes time to set the value. Currently you are trying to read the value before it has been set, use the callback functionality like below.
this.setState({
data: this.props.values
}, () => console.log(this.state.data));
Here are some great articles on setState.
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6
you don't need the ( ) when you set the initial state because it is an object.
export default class Testing extends Component {
state = { //remove (
data: []
}; //remove )
Also worth noting, setState is an async function. You will not be able to getState directly after setState.
In order to get the state right away, you would provide a callback to setState() https://reactjs.org/docs/react-component.html#setstate