I want to get the latest data from a database and render it to my page.
When i load the page for the first time it works fine, but when i change data in the db nothing changes on the page. Even when i call the update function it does not change anything.
The page im posting to has a php function which retrieves data from the db (mysql).
If im doing something wrong, what would be my best option for this?
class Car extends React.Component {
constructor(props) {
super(props);
this.state = {resultState: "red"};
};
componentDidMount() {
axios.post(`https://web.site/page.php`)
.then(res => {
console.log(res.data);
this.setState({ resultState: res.data });
});
}
update() {
this.forceUpdate();
};
render() {
return (<div>
<div className="row" dangerouslySetInnerHTML={{__html: this.state.resultState}}></div>
</div>)
}
}
ReactDOM.render(<Car />, document.getElementById('mydiv'))
Overall, your code works only if you refresh your page.
A simple solution would be pooling the PHP server. Create a load function whose job is to call the PHP server and in your ComponentDidMount function do the following updates
load() {
axios.post(`https://web.site/page.php`)
.then(res => {
console.log(res.data);
// compare if the data has new values
// if it has then update the state i.e. this.setState({ resultState: res.data });
// otherwise do nothing
});
}
componentDidMount() {
this.load();
// This will call the PHP server every 1 minute
setTimeout(this.load(), 1000);
}
Do not use this.forceUpdate(); as it's not recommended by the react team to be used.
Related
I have a React component, that includes the availability flag of Internet connectivity. UI elements have to be dynamically changed according to state real-time. Also, functions behave differently with the changes of the flag.
My current implementation polls remote API using Axios in every second using interval and updates state accordingly. I am looking for a more granular and efficient way to do this task to remove the 1-second error of state with the minimum computational cost. Considered online if and only if device has an external Internet connection
Current implementation :
class Container extends Component {
constructor(props) {
super(props)
this.state = {
isOnline: false
};
this.webAPI = new WebAPI(); //Axios wrapper
}
componentDidMount() {
setInterval(() => {
this.webAPI.poll(success => this.setState({ isOnline: success });
}, 1000);
}
render() {
return <ChildComponent isOnline={this.state.isOnline} />;
}
}
Edited:
Looking for a solution capable of detecting external Internet connectivity. The device can connect to a LAN which doesn't have an external connection. So, it is considered offline. Considers online if and only if device has access to external Internet resources.
You can use https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event
window.addEventListener('offline', (event) => {
console.log("The network connection has been lost.");
});
and https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event
for checking when you're back online
window.addEventListener('online', (event) => {
console.log("You are now connected to the network.");
});
Method one: Using legacy browser API - Navigator.onLine
Returns the online status of the browser. The property returns a boolean value, with true meaning online and false meaning offline. The property sends updates whenever the browser's ability to connect to the network changes. The update occurs when the user follows links or when a script requests a remote page. For example, the property should return false when users click links soon after they lose internet connection.
You can add it to your component lifecycle:
Play with the code below using Chrome dev tools - switch "Online" to "Offline" under the Network tab.
class App extends React.PureComponent {
state = { online: window.navigator.onLine }
componentDidMount() {
window.addEventListener('offline', this.handleNetworkChange);
window.addEventListener('online', this.handleNetworkChange);
}
componentWillUnmount() {
window.removeEventListener('offline', this.handleNetworkChange);
window.removeEventListener('online', this.handleNetworkChange);
}
handleNetworkChange = () => {
this.setState({ online: window.navigator.onLine });
}
render() {
return (
<div>
{ this.state.online ? 'you\'re online' : 'you\'re offline' }
</div>
);
}
}
ReactDOM.render(
<App />
, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
However, I think this isn't what you want, you wanted a real-time connection validator.
Method two: Checking internet connection by using it
The only solid confirmation you can get if the external internet connectivity is working is by using it. The question is which server you should call to minimize the cost?
There are many solutions on the internet for this, any endpoint that responds with a quick 204 status is perfect, e.g.:
calling to Google server (for it being the most battle-tested (?) )
calling its cached JQuery script endpoint (so even if the server is down, you should still be able to get the script as long as you have a connection)
try fetching an image from a stable server (e.g.: https://ssl.gstatic.com/gb/images/v1_76783e20.png + date timestamp to prevent caching)
IMO, if you are running this React app on a server, it makes the most sense to call to your own server, you can call a request to load your /favicon.ico to check the connection.
This idea (of calling your own server) has been implemented by many libraries, such as Offline, is-reachable, and is widely used across the community. You can use them if you don't want to write everything by yourself. (Personally I like the NPM package is-reachable for being simple.)
Example:
import React from 'react';
import isReachable from 'is-reachable';
const URL = 'google.com:443';
const EVERY_SECOND = 1000;
export default class App extends React.PureComponent {
_isMounted = true;
state = { online: false }
componentDidMount() {
setInterval(async () => {
const online = await isReachable(URL);
if (this._isMounted) {
this.setState({ online });
}
}, EVERY_SECOND);
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
return (
<div>
{ this.state.online ? 'you\'re online' : 'you\'re offline' }
</div>
);
}
}
I believe what you have currently is already fine, just make sure that it is calling the right endpoint.
Similar SO questions:
Detect the Internet connection is offline?
Detect network connection in React Redux app - if offline, hide component from user
https://stackoverflow.com/Questions/3181080/How-To-Detect-Online-Offline-Event-Cross-Browser
Setup a custom hook
Setup a hook with the online, offline events. then update a state and return it. This way you can use it anywhere in your app with an import. Make sure you clean up with the return function. If you don't you will add more and more event listeners each time a component using the hook mounts.
const onlineHook = () => {
const {isOnline, setOnline} = React.useState();
React.useEffect(() => {
const goOnline = function(event){
setOnline(true);
});
const goOffline = function(event){
setOnline(false);
});
window.addEventListener('offline', goOffline);
window.addEventListener('online', goOnline);
return () => {
window.removeEventListener('offline', goOffline);
window.removeEventListener('online', goOnline);
}
}, [])
return isOnline
}
To use this just import the above hook and call it like this.
const isOnline = onlineHook(); // true if online, false if not
You can create a component to share between all subcomponents
used:
import React, { useState, useEffect } from "react";
export default function NetworkChecker() {
const [networkStatus, setNetworkStatus] = useState(true)
useEffect(() => {
window.addEventListener('offline', (event) => {
setNetworkStatus(false)
});
window.addEventListener('online', (event) => {
setNetworkStatus(true)
});
return function cleanupListener() {
window.removeEventListener('online', setNetworkStatus(true))
window.removeEventListener('offline', setNetworkStatus(false))
}
},[])
if (networkStatus) {
return <div className={"alert-success"}>Online</div>
} else {
return <div className={"alert-danger"}>Offline</div>
}
}
I'm trying to change the screen when a submit button is pressed. The page is essentially just a spinner page. I want it to change to the page while the data is submittied to the database, then change back once the database stuff is finished.
I can get the screen to change but can't get it to change back. Well, I did once, but as soon as I started adding code to the functions it stopped working entirely.
I have a main homepage that has a table component
class Home extends Component {
constructor() {
super()
this.state = {
data: [],
loading: false
};
}
handleSpinner() {
this.setState({ loading: !this.state.loading })
}
handleCancel() {
if (confirm('Are you sure you want to clear the table?')) {
this.setState({ data: [] })
} else {
return
}
}
render() {
return (
this.state.loading ? <WaitingSpinner /> :
<div>
<MainTable data={this.state.data} handleCancel={this.handleCancel} handleSpinner={this.handleSpinner} />
</div>
);
}
The table has a submit button that calls a submit handler.
handleSubmit() {
this.props.handleSpinner()
this.writeToDatabase(this.props.data)
this.props.handleSpinner()
}
async writeToDatabase(data) {
await data.map(test => {
console.log('DATA ', test)
axios.post(`database stuff')
.then(function (response) { console.log(response) }).catch(function (error) { console.log('Err ', error) })
})
}
My goal here is to change to the spinner, run the database function, once that completes it changes back.
At this point it changes to the spinner screen but never changes back to the main table page. I'm guessing it has something to do with when the state actually gets updated but I don't know enough about it to track down what is going on.
I'm trying to make page pagintation in my model . I've used #material-ui TablePagination . The thing is, I'm using a web service to load my data and I don't want to store all the data inside the page, so I'm using the API paging offered (send parameters to the url for the correct page) .
Now to my code :
<TablePagination
component="div"
count={props.totalTableRows}
rowsPerPage={props.rowsPerPage}
page={props.brokersListPage}
onChangePage={props.setBrokersListPage}
rowsPerPageOptions = {[props.rowsPerPage]}
/>
And the setBrokersListPage :
export const setBrokersListPage = (event, page) => {
return dispatch => {
dispatch({
type: actionNames.SET_BROKERSLIST_PAGE,
page
})
getBrokers(page)
}
}
This code doesn't work . I need the dispatch action to refresh the state of the page , and I need the getBrokers to call the web service once again with the correct info . But all this does is update the page state without updating the data.
If I use this :
export const setBrokersListPage = (event, page) => {
return getBrokers(page)
}
Then the page refreshes , but then the state doesn'
tget refreshed .
How can I achieve both ?
You have to make an API call first, then pass this data to store, connected component will update afterwards. In your code i suppose you don't use the actual data from web service.
Here is simplified example:
Promise.resolve()
.then((page) => {
store.dispatch({
type: LOADING_BROKERS
});
return Api.getbrokers(page);
//get brokers here
})
.then(() => {
store.dispatch({
type: LOADED_DATA_BROKERS,
pageinfo // or whatever
});
})
I have a component that must make an HTTP request based off new props. Currently it's taking a while to actually update, so we've implemented a local store that we'd like to use to show data from past requests and then show the HTTP results once they actually arrive.
I'm running into issues with this strategy:
componentWillRecieveProps(nextProps){
this.setState({data:this.getDataFromLocalStore(nextProps.dataToGet)});
this.setState({data:this.makeHttpRequest(nextProps.dataToGet)});
//triggers single render, only after request gets back
}
What I think is happening is that react bundles all the setstates for each lifecycle method, so it's not triggering render until the request actually comes back.
My next strategy was this:
componentWillRecieveProps(nextProps){
this.setState({data:this.getDataFromLocalStore(nextProps.dataToGet)});
this.go=true;
}
componentDidUpdate(){
if(this.go){
this.setState({data:this.makeHttpRequest(this.props.dataToGet)});
}
this.go=false;
}
//triggers two renders, but only draws 2nd, after request gets back
This one SHOULD work, it's actually calling render with the localstore data immediately, and then calling it again when the request gets back with the request data, but the first render isnt actually drawing anything to the screen!
It looks like react waits to draw the real dom until after componentDidUpdate completes, which tbh, seems completely against the point to me.
Is there a much better strategy that I could be using to achieve this?
Thanks!
One strategy could be to load the data using fetch, and calling setState when the data has been loaded with the use of promises.
componentWillRecieveProps(nextProps){
this.loadData(nextProps)
}
loadData(nextProps){
// Create a request based on nextProps
fetch(request)
.then(response => response.json())
.then(json => this.setState({updatedValue: json.value})
}
I use the pattern bellow all the time (assuming your request function supports promises)
const defaultData = { /* whatever */ }
let YourComponent = React.createClass({
componentWillRecieveProps: function(nextProps) {
const that = this
const cachedData = this.getDataFromLocalStore(nextProps)
that.setState({
theData: { loading: true, data: cachedData }
})
request(nextProps)
.then(function(res) {
that.setState({
theData: { loaded: true, data: res }
})
})
.catch(function() {
that.setState({
theData: { laodingFailed: true }
})
})
},
getInitialState: function() {
return {
theData: { loading: true, data: defaultData }
};
},
render: function() {
const theData = this.state.theData
if(theData.loading) { return (<div>loading</div>) } // you can display the cached data here
if(theData.loadingFailed) { return (<div>error</div>) }
if(!theData.loaded) { throw new Error("Oups") }
return <div>{ theData.data }</div>
}
)}
More information about the lifecycle of components here
By the way, you may think of using a centralized redux state instead of the component state.
Also my guess is that your example is not working because of this line:
this.setState({data:this.makeHttpRequest(this.props.dataToGet)});
It is very likely that makeHttpRequest is asynchronous and returns undefined. In other words you are setting your data to undefined and never get the result of the request...
Edit: about firebase
It looks like you are using firebase. If you use it using the on functions, your makeHttpRequest must look like:
function(makeHttpRequest) {
return new Promise(function(resolve, reject) {
firebaseRef.on('value', function(data) {
resolve(data)
})
})
}
This other question might also help
I'm trying to do two AJAX calls in my React project and have my UI render according to the data received. This is my render method:
render() {
if (this.state.examsLoaded) {
return (
<div>
<Button onClick={this.openModal}>Details</Button>
<Modal show={this.state.modalOpen} onHide={this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>{this.props.course.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<DetailModalContent course={this.props.course} exams={this.exams} grades={this.grades}/>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.closeModal}>Sluiten</Button>
</Modal.Footer>
</Modal>
</div>
)
}
else {
return (
<div>Loading...</div>
)
}
}
The render method checks if the AJAX data is available yet and if not, just renders a 'Loading...' message. This is the code that fetches the data:
componentDidMount() {
fetch('http://localhost:8080/course/' + this.props.course.id + '/exams').then((examResp) => {
examResp.json().then((examData) => {
this.exams = examData;
console.log('Course data fetched'); // THIS APPEARS
fetch('http://localhost:8080/user/1/grades').then((gradeResponse) => { // THIS DATA IS FETCHED
console.log('Done fetching grades'); // THIS APPEARS
gradeResponse.json((gradeData) => {
console.log('Parsed JSON'); // Here is where it goes wrong. This no longer appears.
this.grades = gradeData;
this.setState({
examsLoaded: true,
modalOpen: false
});
});
});
});
});
},
The weird thing is, I used to only have 1 fetch method and everything would work fine. As soon as I called setState the component rerenders and the data is displayed. However, after adding the second one, it doesn't work anymore. See my console.log's. Everything works fine 'till I parse the JSON, after that, nothing gets run anymore.
What am I doing wrong?
Thanks!
fetch's json() method returns a promise. You are using it correctly in the first call, but the second call you are treating it as a function rather than a promise.
Try
gradeResponse.json().then((gradeData) => {
...
});
You need to write this logic inside componentDidUpdate. componentDidMount will be triggered only for the first time.
Please refer to the React documentation.
Probably you will need both componentDidMount and componentDidUpdate.
componentDidMount() {
fetch('http://localhost:8080/course/' + this.props.course.id + '/exams').then((examResp) => {
examResp.json().then((examData) => {
this.exams = examData;
console.log('Course data fetched'); // THIS APPEARS
this.setState({
examsLoaded: true
}); //At this point some state is changed, so componentDidUpdate will be triggered. Then in that function below, grades will be fetched and state is changed, which should call render again.
});
});
},
componentDidUpdate(){
fetch('http://localhost:8080/user/1/grades').then((gradeResponse) => { // THIS DATA IS FETCHED
console.log('Done fetching grades'); // THIS APPEARS
gradeResponse.json((gradeData) => {
console.log('Parsed JSON'); // Here is where it goes wrong. This no longer appears.
this.grades = gradeData;
this.setState({
examsLoaded: true,
modalOpen: false
});
});
});
}
Since I am not with react environment right now. Will update as soon as I try.