When you call it in http://localhost:9000/testApi, it works fine.
testAPI.js
const express = require('express');
var router = express.Router();
router.get("/",function(req,res){
res.send("API is working fine");
});
module.exports = router;
But Calling in ReactJS functional component leads to nothing
import React, {useState, useEffect} from 'react';
import TopicCard from './TopicCard.js'
import './HomePage.css'
function HomePage() {
const [apiResponse,setApiResponse] = useState('Loading..')
const url = "http://localhost:9000/"
useEffect(() => {
fetch(url).then(res => setApiResponse(res.data))
}, [])
return (
<>
<h1>Choose a topic to learn {apiResponse}</h1>
</>
);
Console.log gives this
PromiseĀ {}[[Prototype]]: Promise [[PromiseState]]: "rejected"
[[PromiseResult]]: SyntaxError: Unexpected token A in JSON at position
0
While the Class Component is working perfectly fine
import React, {Component} from 'react'
class Test extends Component {
constructor(props) {
super(props);
this.state = { apiResponse: "" };
}
callAPI() {
fetch("http://localhost:9000/testAPI")
.then(res => res.text())
.then(res => this.setState({ apiResponse: res }));
}
componentWillMount() {
this.callAPI();
}
render()
{
return (
<div>
<p className="App-intro">;{this.state.apiResponse}</p>
</div>
);
}
}
export default Test
No differences are between functional and class-based components.
The Problem
You forgot to parse your response as a text in the fetch method.
The Solution
parse your data as a text and then store it on your state variable
useEffect(() => {
fetch(URL)
.then(res => res.text())
.then(res => setApiResponse(res))
.catch(err => console.warn(err))
}, [])
Note: don't forget to use catch method for your asynchronous fetch API.
Explanation
When your data (API call response) is in standard JSON format, you need to parse them with .json() method, and usually, a data property holds the whole response, but in your case (with a text as a response) it's not useful.
Are you confusing routes / end points with file names? testAPI.js is your file name. It's not your endpoint.
You call:
const url = "http://localhost:9000/testAPI"
useEffect(() => {
fetch(url).then(res => setApiResponse(res.data))
}, [])
But your endpoint is - a forward slash '/' i.e. the root (not ROUTE) :
router.get("/",function(req,res){
res.send("API is working fine");
});
Try changing to this:
const url = "http://localhost:9000/"
useEffect(() => {
fetch(url).then(res => setApiResponse(res.data))
}, [])
If you want to fetch const url = "http://localhost:9000/testAPI" from react then change the endpoint to:
const url = "http://localhost:9000/testAPI" else server won't know of it.
Related
I try to fill a React DataGrid with data from a JSON provided by node backend.
The backend code looks as follows:
app.get("/articles", (req, res) => {
res.json([
{
"title":"Test Article One",
"timestamp":"09-01-2023",
"text":"Test text one"
},
{
"title":"Test Article Two",
"timestamp":"10-01-2023",
"text":"Test text two"
},
{
"title":"Test Article Three",
"timestamp":"11-01-2023",
"text":"Test text three"
}])
})
The React code looks as follows:
const MemberPage = () => {
const [articles, setArticles] = useState([])
const [articleKeys, setArticleKeys] = useState([])
useEffect(() => {
fetch("http://localhost:3001/articles")
.then((res) => res.json())
.then((data) => {
setArticles(data)
setArticleKeys(Object.keys(data[0]))
})
})
return (
<div id="memberpage-main-container">
<DataGrid columns={articleKeys} rows={articles} />
</div>
)
}
I get the error message TypeError: null is not an object (evaluating 'measuringCell.getBoundingClientRect') in the browser console and the page wouldn't render. I first thought, it is because the DataGrid is rendered before the useEffect fetches the data which I've red in other answers, however, when I write:
const articleKeys = []
const articles = []
it works (I'm mean, it's an empty page then, but I don't get any errors). So, I would expect it not to be a problem when setting useState([]).
Any help is appreciated.
So, I've found a solution by switching from react-data-grid to #mui/x-data-grid.
The code now looks as follows:
import React, { useState, useEffect } from "react"
import { DataGrid } from "#mui/x-data-grid"
const MemberPage = () => {
const [articles, setArticles] = useState([])
const [articleKeys, setArticleKeys] = useState([])
function parseArticleKeys(keys) {
let tableColumns = []
for (let i = 0; i < keys.length; i++) {
tableColumns.push({field: keys[i], headerName: keys[i], width: 300})
}
return tableColumns
}
useEffect(() => {
fetch("http://localhost:3001/articles")
.then((res) => res.json())
.then((data) => {
setArticles(data)
setArticleKeys(Object.keys(data[0]))
})
})
return (
<div id="memberpage-news-container">
<DataGrid columns={parseArticleKeys(articleKeys)} rows={articles} />
</div>
)
}
export default MemberPage
I tried a similar thing with react-data-grid but couldn't get it to work. If someone has an idea to accomplish that with react-data-grid, it still might be helpful for others but I personally am ok with that solution.
I was trying to display an array of data fetched from my custom server with RTK Query using Next.js (React framework). And this is my first time using RTK Query. Whenever I console.log the data, it appears in the browser console. But whenever I try to map the data to render it in the browser, it keeps throwing an error saying Cannot read properties of undefined (reading 'map').
I figured Next.js always throws an error if an initial state is undefined or null even if the state change. This link talked about solving the problem using useMemo hook https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics
But I didn't understand it well. Please kindly help me out with displaying the data.
Here is the BaseQuery function example I followed, it was derived from redux toolkit docmentation https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#axios-basequery
import axios from "axios";
const axiosBaseQuery =
({ baseUrl } = { baseUrl: "" }) =>
async ({ url, method, data }) => {
try {
const result = await axios({ url: baseUrl + url, method, data });
return { data: result.data };
} catch (axiosError) {
let err = axiosError;
return {
error: { status: err.response?.status, data: err.response?.data },
};
}
};
export default axiosBaseQuery;
I make the GET request here
import { createApi } from "#reduxjs/toolkit/query/react";
import axiosBaseQuery from "./axiosBaseQuery";
export const getAllCarsApi = createApi({
reducerPath: "getAllCarsApi",
baseQuery: axiosBaseQuery({
baseUrl: "http://localhost:5000/",
}),
endpoints(build) {
return {
getAllCars: build.query({
query: () => ({ url: "all-cars", method: "get" }),
}),
};
},
});
export const { useGetAllCarsQuery } = getAllCarsApi;
This is my redux store
import { configureStore } from "#reduxjs/toolkit";
import { getAllCarsApi } from "./getAllCarsApi";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
const store = configureStore({
reducer: { [getAllCarsApi.reducerPath]: getAllCarsApi.reducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(getAllCarsApi.middleware),
});
setupListeners(store.dispatch);
export default store;
I provide the store to the _app.js file.
import "../styles/globals.css";
import axios from "axios";
import { MyContextProvider } from "#/store/MyContext";
import { Provider } from "react-redux";
import store from "#/store/ReduxStore/index";
axios.defaults.withCredentials = true;
function MyApp({ Component, pageProps }) {
return (
<MyContextProvider>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</MyContextProvider>
);
}
export default MyApp;
I get the data here in my frontend.
import { useGetAllCarsQuery } from "#/store/ReduxStore/getAllCarsApi";
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
export default theTest;
This is a timing thing.
Your component will always render immediately and it will not defer rendering until data is there. That means it will also render before your data has been fetched. So while the data is still loading, data is undefined - and you try to map over that.
You could do things like just checking if data is there to deal with that:
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data && data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
I am new to React/redux with Node. I am working on a full stack app that utilizes Node.js on the server side and React/Redux on the client side. One of the functions of the app is to provide a current and eight-day weather forecast for the local area. The Weather route is selected from a menu selection on the client side that menu selection corresponds to a server side route that performs an axios.get that reaches out and consumes the weather api (in this case Darksky) and passes back that portion of the JSON api object pertaining to the current weather conditions and the eight-day weather forecast. There is more to the API JSON object but the app consume the "current" and "daily" segment of the total JSON object.
I have written a stand-alone version of the server-side axios "get" that successfully reaches out to the Darksky API and returns the data I am seeking. I am, therefore, reasonably confident my code will correctly bring back the data that I need. My problem consists in this: when I try to render the data in my React Component, the forecast object is undefined. That, of course, means there is nothing to render.
I have reviewed my code, read a plethora of documentation and even walked through tutorials that should help me find the problem and it still eludes me. So, I am stuck and would greatly appreciate some help. Most of the comment you still in the code below will be removed after the debugging process is completed.
I am including code blocks relevant to the problem:
My React Component
// client/src/components/pages/functional/Weather.js
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import Spinner from '../../helpers/Spinner'
import { getWeather } from '../../../redux/actions/weather'
const Weather = ({ getWeather, weather: { forecast, loading } }) => {
// upon load - execute useEffect() only once -- loads forecast into state
useEffect(() => { getWeather(); }, [getWeather])
return (
<div id='page-container'>
<div id='content-wrap' className='Weather'>
{ loading ?
<Spinner /> :
<>
<div className='WeatherHead box mt-3'>
<h4 className='report-head'>Weather Report</h4>
</div>
{/* Current Weather Conditions */}
<h6 className='current-head'>Current Conditions</h6>
<section className='CurrentlyGrid box mt-3'>
/* additional rendering code removed for brevity */
<span><Moment parse='HH:mm'>`${forecast.currently.time}`</Moment></span>
/* additional rendering code removed for brevity */
</section>
</>
}
</div>
</div>
);
};
Weather.propTypes = {
getWeather: PropTypes.func.isRequired,
weather: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ forecast: state.forecast });
export default connect( mapStateToProps, { getWeather } )(Weather);
My React Action Creator
// client/src/redux/actions/weather.js
import axios from 'axios';
import chalk from 'chalk';
// local modules
import {
GET_FORECAST,
FORECAST_ERROR
} from './types';
// Action Creator
export const getWeather = () => async dispatch => {
try {
// get weather forecast
const res = await axios.get(`/api/weather`);
console.log(chalk.yellow('ACTION CREATOR getWeather ', res));
// SUCCESS - set the action -- type = GET_WEATHER & payload = res.data (the forecast)
dispatch({
type: GET_FORECAST,
payload: res.data
});
} catch (err) {
// FAIL - set the action FORECAST_ERROR, no payload to pass
console.log('FORECAST_ERROR ',err)
dispatch({
type: FORECAST_ERROR
});
};
};
My React Reducer
// client/src/redux/reducers/weather.js
import {
GET_FORECAST,
FORECAST_ERROR,
} from '../actions/types'
const initialState = {
forecast: null,
loading: true
}
export default (state = initialState, action) => {
const { type, payload } = action
switch (type) {
case GET_FORECAST:
return {
...state,
forecast: payload,
loading: false
}
case FORECAST_ERROR:
return {
...state,
forecast: null,
loading: false
}
default:
return state
}
}
My Node Route
// server/routes/api/weather.js
const express = require('express');
const axios = require('axios');
const chalk = require('chalk');
const router = express.Router();
// ***** route: GET to /api/weather
router.get('/weather', async (req, res) => {
try {
// build url to weather api
const keys = require('../../../client/src/config/keys');
const baseUrl = keys.darkskyBaseUrl;
const apiKey = keys.darkskyApiKey;
const lat = keys.locationLat;
const lng = keys.locationLng;
const url = `${baseUrl}${apiKey}/${lat},${lng}`;
console.log(chalk.blue('SERVER SIDE ROUTE FORECAST URL ', url));
const res = await axios.get(url);
// forecast -- strip down res, only using currently{} & daily{}
const weather = {
currently: res.data.currently,
daily: res.data.daily.data
};
console.log(chalk.yellow('SERVER SIDE ROUTE FORECAST DATA ', weather));
// return weather
res.json({ weather });
} catch (error) {
console.error(chalk.red('ERR ',error.message));
res.status(500).send('Server Error');
}
});
module.exports = router;
My Express server middleware pertaining to routes (just to be thorough)
// server/index.js
/* code deleted for brevity */
// define routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/weather', require('./routes/api/weather'));
app.use('/api/favorites', require('./routes/api/favorites'));
/* code deleted for brevity */
If the code snippets included are not sufficient, the repo resides here: https://github.com/dhawkinson/TH12-BnBConcierge
Thank you in advance for help with this.
***** Updates *****
I notice that the console logs I have in both actions/weather.js & reducers/weather.js on the client side & routes/api/weather.js on the server side are NOT firing. That tells me that those modules must not be executing. That would explain why I am getting the error "Cannot read property 'currently' of undefined" in client/src/components/pages/functional/Weather.js. Clearly I have a missing link in this chain. I just can't see what it is.
I tried a small refactor, based on input below. I was trying to see if there was some kind of naming conflict going on. this is what I did in my React functional Component:
// client/src/components/pages/functional/Weather.js
...
const mapStateToProps = state => ({weather: { forecast: state.forecast, loading: state.loading }});
...
It didn't help.
I see that in your combineReducers here you are setting as
export default combineReducers({
alert,
auth,
weather
})
So in the store, things gets saved as { alert: {...}, auth: {...}, weather: {...}}. Can you try accessing the forecast value in your Weather as state.weather.forecast ?
const mapStateToProps = state => ({ forecast: state.weather.forecast });
Let me know if it works.
You need to modify your component.
const dispatch = useDispatch();
useEffect(() => { dispatch(getWeather()); }, [getWeather])
And your mapToStateToProps should be as follows:
const mapStateToProps = state => ({ forecast: state.weather.forecast });
While trying to learn full stack development I was trying out this tutorial ( https://www.freecodecamp.org/news/create-a-react-frontend-a-node-express-backend-and-connect-them-together-c5798926047c/ ) on a React-Express-Node basic app. However, it was written using functional components instead of hooks. I'm trying to convert this section to a hook:
constructor(props) {
super(props);
this.state = { apiResponse: "" };
}
callAPI() {
fetch("http://localhost:9000/testAPI")
.then(res => res.text())
.then(res => this.setState({ apiResponse: res }));
}
componentWillMount() {
this.callAPI();
}
with this in the render section:
<p className="App-intro">;{this.state.apiResponse}</p>
I tried this:
const [apiResponse, setApiResponse] = useState();
useEffect(() => {
const fetchApiResponse = async () => {
const result = await (
'http://localhost:9000/testAPI'
);
setApiResponse(result);
console.log("apiResponse " + apiResponse);
};
fetchApiResponse();
});
but the console.log of the apiResponse always shows as undefined. I know I must be doing something wrong but I can't figure it out.
You aren't far off in your attempt.
There are two problems:
Problem 1.
In order to get the same effect as componentWillMount (side note - this is a deprecated method, use componentDidMount or the constructor) you need to tell the useEffect to only run once on mount. To do this you give it an empty array of dependencies.
useEffect(() => {
// do stuff
}, []); // empty array as second argument
By not giving a second argument, the effect will run every single render.
Problem 2.
State updates are asynchronous. This means you cannot console log apiResponse immediately after updating it and expect it to contain the new value.
To get around this, just console.log inside the function body outside of the hook.
Here is a simplified example:
const {useState, useEffect} = React;
const Example = () => {
const [apiResponse, setApiResponse] = useState();
useEffect(() => {
const fetchApiResponse = () => {
const result = 'test';
setApiResponse(result);
// Will not be updated
console.log("wrong: apiResponse ", apiResponse);
}
fetchApiResponse();
}, []);
// Will be updated
console.log("right: apiResponse ", apiResponse);
return <span />
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am using InfluxDB as database, I used influxdb-nodejs library module to create API to write data and Query data from Influxdb.
The Queryfunction.js API code is as follows:
module.exports = {
queryfunc: function() {
const Influx = require('influxdb-nodejs');
const client = new Influx('http://127.0.0.1:8086/mydb');
client.query('http')
.where('type', '2')
.then(console.info)
.catch(console.error);
}
}
I use a script.js file to call queryfunc() in Queryfunction.js API:
const myModule = require('./Queryfunction');
let val = myModule.queryfunc();
I use command node script to run the script file.
The result is an array
C:\Users\Admin\Desktop\reactApp\API>node script
{ results: [ { statement_id: 0, series: [Array] } ] }
I am using ReactJS to create front end UI components. How to fetch the resultant array data in ReactJS?
You're either need to write to a json file or you'll need to have an express wrapper around your db calls.
const express = require('express')
const app = express();
app.get('/api', (req, res, next) => {
const Influx = require('influxdb-nodejs');
const client = new Influx('http://127.0.0.1:8086/mydb');
client.query('http')
.where('type', '2')
.then(data => res.json(data)
.catch(err => next(err)); // error middleware to handle
}
app.listen('3000', () => console.log('running on http://localhost:3000'))
Within react you do a fetch:
Class App extends React.Component {
componentDidMount() {
fetch('http://localhost:3000')
.then(res => res.json())
.then(data => this.setState( {data} ) )
render() {
....
}
}