I'm using Expo Go to test out my React Native app. I'm fetching a request from a website using the Fetch API. On the first time that I fetch the data, it seems to be returning {"next": null, "parent": {"children": [[Circular]], "endIndex": null, "next": null, "parent": null, "prev": null, "startIndex": null, "type": "root"}, "prev": null} or sometimes {}.
However, if I Ctrl+S, which hot reloads the application, it pulls in all the required data from the fetch request.
import React, { useEffect, useState } from 'react'
import { ActivityIndicator, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
const EventsScreen = () => {
const [eventsHTML, setEventsHTML] = useState({});
const {width} = useWindowDimensions();
const ParseEventsHTML = ()=>{
console.log(eventsHTML);
}
const RefreshEvents = ()=>{
console.log("refreshing events");
fetch('https://www.thesubath.com/whats-on/')
.then(response => response.text())
.then(textResponse => {
setEventsHTML(textResponse);
ParseEventsHTML();
});
}
useEffect(()=>{
RefreshEvents();
}, []);
return (
<View>
{!eventsHTML? <ActivityIndicator/> : <Text>Hello world</Text>}
</View>
)
}
export default EventsScreen;
Package.json
{
"name": "bathe",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"#react-navigation/bottom-tabs": "^6.5.3",
"#react-navigation/drawer": "^6.5.7",
"#react-navigation/native": "^6.1.2",
"#react-navigation/native-stack": "^6.9.8",
"#reduxjs/toolkit": "^1.9.2",
"cheerio": "^1.0.0-rc.12",
"expo": "~47.0.12",
"expo-status-bar": "~1.4.2",
"firebase": "^9.16.0",
"htmlparser2": "^8.0.1",
"nativewind": "^2.0.11",
"react": "18.1.0",
"react-native": "0.70.5",
"react-native-gesture-handler": "~2.8.0",
"react-native-html-parser": "^0.1.0",
"react-native-reanimated": "~2.12.0",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-redux": "^8.0.5"
},
"devDependencies": {
"#babel/core": "^7.12.9"
},
"private": true
}
I think your issue is around when your eventsHTML piece of state is actually being updated. If you take a look at the setState docs, you'll see the first Caveat reads:
The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call.
You need to refactor where you are running your console.log so that you can make sure you actually get it when that value updates. I might try adding another useEffect to log each time the eventsHTML state updates:
useEffect(() => {
console.log("eventsHTML changed: ", eventsHTML);
}, [eventsHTML]);
Also, I can say that your <View> component is never going to show the <ActivityIndicator> on the first load as you are setting it equal to an empty object from the start, so !eventsHTML will never actually return truthy. You may want to remove the {} from your useState method:
const [eventsHTML, setEventsHTML] = useState();
This will make sure that your variable remains null until you actually populate it with data.
Related
I am trying to call useEffect() whenever arrayWithDeeplyNestedObjects changes. export default compose(... is part of an offline first database (watermelonDB) and updates arrayWithDeeplyNestedObjects when there is a change in the database. One could expect useEffect() to execute, whenever arrayWithDeeplyNestedObjects changes, but it is not. This is beause useEffect() only performs a shallo comparison and does not recognize the changes in arrayWithDeeplyNestedObjects.
import withObservables from '#nozbe/with-observables';
import {withDatabase} from '#nozbe/watermelondb/DatabaseProvider';
import {compose} from 'recompose';
import {Q} from '#nozbe/watermelondb';
const Foo = ({arrayWithDeeplyNestedObjects}) => {
console.log('render'); // <-- this renders whenever arrayWithDeeplyNestedObjects is updated
useEffect(() => {
console.log(new Date()); // <-- this does not render whenever arrayWithDeeplyNestedObjects is updated
const doSomething = async () => {
...
};
const data = await doSomething();
setData(data);
}, [arrayWithDeeplyNestedObjects]); // <-- this does only perform a shallow compare
return <SomeNiceUi />
}
export default compose(
withDatabase,
withObservables(['arrayWithDeeplyNestedObjects'], ({database}) => ({
arrayWithDeeplyNestedObjects: database.get(SOME_TABLE).query().observe(),
})),
)(Foo); <-- subscription das fires everytime an update is made to the database
This is how arrayWithDeeplyNestedObjects looks like
[{"__changes": null, "_isEditing": false, "_preparedState": null, "_raw": {"_changed": "x,y", "_status": "created", "id": "3", "x": 5851, "id_arr": "[\"160\"]", "id": "6wmwep5xwfzj3dav", "y": 0.17576194444444446}, "_subscribers": [], "collection": {"_cache": [RecordCache], "_subscribers": [Array], "changes": [Subject], "database": [Database], "modelClass": [Function SomeTable]}}]
The changes to arrayWithDeeplyNestedObjects are done in the objects either to x, y or id_arr. The length of arrayWithDeeplyNestedObjects can change as well. There might be more (or less) objects of the same structure in there.
How to call useEffect() everytime arrayWithDeeplyNestedObjects changes?
try this:
useEffect(() => {
console.log(new Date());
const doSomething = async () => {
...
};
doSomething();
}, [JSON.stringify(arrayWithDeeplyNestedObjects)]);
I have a simple program which receives some JSON data from a node backend and set received data into state. The problem is it reset state infinite times, creating an infinite rendering.
Here is the JSON data
[
{
"id": 1,
"name": "Product 1",
"category": "C1",
"price": "100"
},
{
"id": 2,
"name": "Product 2",
"category": "C1",
"price": "80"
},
{
"id": 3,
"name": "Product 3",
"category": "C3",
"price": "120"
}
]
Here is the react program.
import React, { useState } from 'react'
const MainApp = () => {
const [products, setProducts] = useState([])
fetch("http://localhost:5000/products")
.then((res) => res.json())
.then((res) => {setProducts(res)})
.catch((err) => console.error(err))
console.log("Products:",products) //This keep getting logged forever.
return (
<h1>Test</h1>
)
}
export default MainApp
What have I done wrong?
The fetch is continuously performed on every render of MainApp. Consider using an effect to solve this.
You should only call fetch when components mounts. Since you are using hooks, you should use `
useEffect(()=> {
fetch("http://localhost:5000/products")
.then((res) => res.json())
.then((res) => {setProducts(res)})
.catch((err) => console.error(err))
}, [])
`
What you are doing right now, is calling fetch on every render. Imagine it like this. You are rendering the component, during that you are fetching something and updating the state. When the state updates, it rerenders the components and you are going on an infinite loop.
the problem is in {setProducts(res)} this will update the state and re-render the component then call the fetch function second time and so on
I am converting an React project from JS to Typescript. To organize the code I am using Function Components with custom hook functions to fetch the data as shown below. However, upon using these functions am always getting the above trivial error. I have found the official react page about mismatching version of react but it seems not the case in my situation (https://reactjs.org/warnings/invalid-hook-call-warning.html) . Any idea what might be the problem?
import { useEffect, useState } from 'react';
const usePostItemService = () => {
const [result, setResult] = useState<>({
status: 'loading'
});
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(response => setResult({ status: 'loaded', payload: response }))
.catch(error => setResult({ status: 'error', error }));
}, []);
return result;
};
export default usePostItemService;
The above custom hook is simply called in function component as below:
const Items: React.FC<{}> = () => {
const service = usePostItemService();
return (
<div>
Hello
</div>
);
};
export default Items;
The dependencies are currently set as follows:
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.1.2"
I'm having some trouble with ESLint and arrow functions in a react component using the eslint-plugin-react plugin. I just did these commands:
npm i -g eslint eslint-plugin-react
eslint SignUpPage.jsx
And here is SignUpPage:
/**
* Created by jwilso37 on 4/5/2017.
*/
import React from 'react';
import SignUpForm from '../components/landing/SignUpForm.jsx';
import 'whatwg-fetch'
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: {},
user: {
email: '',
name: '',
password: ''
}
};
}
/**
* Change the user object.
*
* #param {object} e - the JavaScript event object
*/
changeUser = (e) => {
const field = e.target.name;
const user = this.state.user;
user[field] = e.target.value;
this.setState({
user
});
};
/**
* Handles processForm event and submits request to server.
* #param e
*/
processForm = (e) => {
e.preventDefault();
const form = document.querySelector('form');
const formData = new FormData(form);
fetch('/api/signup', {
method: 'POST',
body: formData
}).then(response => {
if (response.ok) {
this.setState({
errors: {}
});
}
else {
// returned > 300 status code
response.json().then(j => {
const errors = j.errors ? j.errors : {};
errors.summary = j.message;
this.setState({
errors: errors
})
})
}
})
};
/**
* Render the component.
*/
render() {
return (
<SignUpForm
onSubmit={this.processForm}
onChange={this.changeUser}
errors={this.state.errors}
user={this.state.user}
/>
);
}
}
export default SignUpPage;
But the output of eslint is oddly this:
ubuntu#ETFly:/vagrant/client/src/containers$ eslint signuppage.jsx
/vagrant/client/src/containers/signuppage.jsx
31:16 error Parsing error: Unexpected token =
Saying that the = sign on the changeUser anonymous function is an error? Here's my .eslintrc.json:
{
"plugins": [
"react"
],
"settings": {
"react": {
"createClass": "createClass", // Regex for Component Factory to use, default to "createClass"
"pragma": "React", // Pragma to use, default to "React"
"version": "15.0" // React version, default to the latest React stable release
}
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 6,
"ecmaFeatures": {
"jsx": true
}
},
"extends": ["eslint:recommended", "plugin:react/recommended"],
"env": {
"browser": true
}
}
OK I seemed to have fixed it. I installed babel-eslint with npm i --save-dev babel-eslint after I realized my JSX spread operator wasn't working as well. It made me think that all of ES6 stuff was messed up. So now I just added babel-eslint parser with adding "parser": "babel-eslint" to the end of my .eslintrc.json file and everything is good! I guess the default eslint parser doesn't support these features...
Here is the class with the static function
import alt from '../alt';
import Parse from 'parse';
import { ElementTypes } from '../constants/ElementTypes';
class BoardActions {
static getDefaultElement(x, y) {
var Element = Parse.Object.extend("Element");
var element = new Element();
element.set("x", x);
element.set("y", y);
return element;
}
}
export default alt.createActions(BoardActions);
And this is the class who calls the static function const startElement = BoardActions.getDefaultElement(0, 3);
import alt from '../alt';
import Parse from 'parse';
import { ElementTypes } from '../constants/ElementTypes';
import BoardActions from './BoardActions';
class ProjectActions {
createNewProject(name) {
return (dispatch) => {
dispatch();
const Project = Parse.Object.extend("Project");
const project = new Project();
let projectObject = null;
project.set('name', name);
project.save().then((object) => {
projectObject = object;
const startElement = BoardActions.getDefaultElement(0, 3);
startElement.set('type', ElementTypes.StartType);
startElement.set('root', true);
startElement.set('projectId', object.id);
return startElement.save();
}).then((object) => {
this.newProjectCreated(projectObject);
}, (error) => {
this.parseError(error);
});
}
}
}
export default alt.createActions(ProjectActions);
I get this error:
ProjectActions.js:69 Uncaught TypeError: _BoardActions2.default.getDefaultElement is not a function
What's wrong?
Edit:
I use babel as transpiler.
"babel-core": "^6.5.1",
"babel-loader": "^6.2.2",
"babel-preset-es2015": "^6.5.0",
"babel-preset-react": "^6.5.0",
"babel-preset-react-hmre": "^1.1.0",
"babel-preset-survivejs-kanban": "^0.3.3",
EDITED (since you edited your question):
In the second file you are importing
import BoardActions from './BoardActions';
Which is importing the default from './BoardActions'.
Looking at the first file you are exporting the result of a function rather than the class itself.
export default alt.createActions(BoardActions);