I'm working with Open Weather API, but when i send an request for a city data, i get network error.
Here is the action which does the job of getting the resource.
actions/index.js
import axios from "axios";
const ROOT_URL = `https://samples.openweathermap.org/data/2.5/forecast?appid=${API_KEY}`;
export const FETCH_WEATHER = "FETCH_WEATHER";
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city},us`;
const request = axios.get(url);
console.log("request:", request);
return {
type: FETCH_WEATHER,
payload: request
};
}
When i press the submit button then i can see error in the mozilla firefox console.
something like this...Error:network error
but i want the city name under the city header...
just like this...
axios.get(url) is returning promise,
conventional way is,
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city},us`;
const res = axios.get(url).then(function(res){
console.log("response:", res);
return {
type: FETCH_WEATHER,
payload: res
};
}).catch(err){
console.log(err)
}
}
OR,
Use async/await to get required result.
export async function fetchWeather(city) {
try{
const url = `${ROOT_URL}&q=${city},us`;
const res = await axios.get(url);
console.log("request:", res);
return {
type: FETCH_WEATHER,
payload: res
};
}catch(err){
console.log(err)
}
}
In your actions/index.js put this:
import axios from "axios";
const API_KEY = "YOUR API KEY GOES HERE ...";
const ROOT_URL = `https://api.openweathermap.org/data/2.5/forecast?appid=${API_KEY}&units=metric`;
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
const request = axios.get(url);
return {
type: FETCH_WEATHER,
payload: request,
};
}
In your reducers/reducer_weather.js put this:
import { FETCH_WEATHER } from "../actions/index";
export default function (state = [], action) {
if (action.error) {
return state;
}
switch (action.type) {
case FETCH_WEATHER:
return [action.payload.data, ...state];
}
return state;
}
Additionally, make sure to include your weather reducer inside of you root reducer, for example:
reducers/index.js
import { combineReducers } from "redux";
import WeatherReducer from "./reducer_weather";
const rootReducer = combineReducers({
weather: WeatherReducer
});
export default rootReducer;
You have built your URL wrong way, and you can not query your initial URL, it is just a sample ready for download.
import axios from 'axios';
const ROOT_URL = 'http://api.openweathermap.org/data/2.5/forecast';
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city) {
const url = `${ROOT_URL}?q=${city},us&APPID=${API_KEY}`;
const request = axios.get(url);
console.log('request:', request);
return {
type: FETCH_WEATHER,
payload: request
};
}
Regards.
Related
I have been trying to introduce redux-sagas and redux-toolkit to my project. It seems when I dispatch(fetchTweets(term)) I can see the actions firing off in the Redux DevTools. But when it gets to the saga nothing happens. Not sure how to fix it. Let me know if you have any ideas. Here is the error I am getting. Here is the link to github
file - configureAppStore.js
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import reducer from './reducer';
import toast from './middleware/toast.js';
import websocket from './middleware/websocket.js';
import createSagaMiddleware from 'redux-saga';
import tweetSagas from '../saga/tweet.js';
const configureAppStore = () => {
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware, websocket, toast];
const middleware = [
...getDefaultMiddleware({ thunk: false }),
...middlewares,
];
const store = configureStore({
reducer: reducer,
middleware: middleware,
});
sagaMiddleware.run(tweetSagas);
return store;
};
export default configureAppStore;
file - saga/tweet.js
import { takeEvery, call, put, fork } from 'redux-saga/effects';
import axios from 'axios';
import * as actions from '../store/action/saga.js';
const port = process.env.REACT_APP_PORT;
const hostname = process.env.REACT_APP_LOCALHOST;
const baseURL = `http://${hostname}:${port}`;
function api({ dispatch }) {
return function (next) {
return function* (action) {
if (action.type !== actions.sagaApiCallBegan.type) return next(action);
next(action); // 'sagaApiCallBegan' to show in redux dev tools
const { url, method, onSuccess, onError } = action.payload;
try {
const response = yield call(
async () =>
await axios.request({
baseURL: baseURL,
url,
method,
})
);
if (onSuccess) yield put({ type: onSuccess, payload: response.data });
} catch (error) {
if (onError) yield put({ type: onError, payload: error });
}
};
};
}
function* watchApi() {
yield takeEvery(actions.sagaApiCallBegan.type, api);
}
export default function* tweetSagas() {
yield fork(watchApi);
}
file- store/tweets.js
import { createSlice } from '#reduxjs/toolkit';
import {
sagaApiCallBegan,
sagaApiCallSuccess,
sagaApiCallFailed,
} from './action/saga';
import { webSocketCallBegan, webSocketCallFailed } from './action/websocket.js';
import { normalize } from 'normalizr';
import { tweetSchema } from '../store/Schema/tweet.js';
const initialState = () => ({
byTweetId: {},
byUserId: {},
allTweetIds: [],
});
// action, actionTypes and reducer
const slice = createSlice({
name: 'tweets',
initialState: initialState(),
// reducers
reducers: {
tweetAdded: (state, action) => {
const { entities, result } = normalize(action.payload, tweetSchema);
Object.assign(state.byTweetId, entities.byTweetId);
Object.assign(state.byUserId, entities.byUserId);
state.allTweetIds.push(result);
},
tweetStoreReseted: (state) => initialState(),
},
});
export const { tweetAdded, tweetStoreReseted } = slice.actions;
export default slice.reducer;
// Action creators
export const fetchTweets = (term) =>
sagaApiCallBegan({
url: `/setsearchterm/${term}`,
method: 'get',
onSuccess: sagaApiCallSuccess.type,
onError: sagaApiCallFailed.type,
});
export const fetchTweetsPause = () =>
sagaApiCallBegan({
url: '/pause',
method: 'GET',
onSuccess: sagaApiCallSuccess.type,
onError: sagaApiCallFailed.type,
});
export const getTweet = (message) =>
webSocketCallBegan({
message: message,
onSuccess: tweetAdded.type,
onError: webSocketCallFailed.type,
});
file - action/saga.js
import { createAction } from '#reduxjs/toolkit';
export const sagaApiCallBegan = createAction('saga/apiCallBegan');
export const sagaApiCallSuccess = createAction('saga/apiCallSuccess');
export const sagaApiCallFailed = createAction('saga/apiCallFailed');
Here is the solution
file - saga/tweet.js
import { takeEvery, call, put, fork } from 'redux-saga/effects';
import axios from 'axios';
import * as actions from '../store/action/saga.js';
const port = process.env.REACT_APP_PORT;
const hostname = process.env.REACT_APP_LOCALHOST;
const baseURL = `http://${hostname}:${port}`;
const fetchApi = async ({ baseURL, url, method }) =>
await axios.request({
baseURL: baseURL,
url: url,
method: method,
});
function* api(action) {
const { url, method, onSuccess, onError } = action.payload;
const options = {
baseURL: baseURL,
url: url,
method: method,
};
try {
const response = yield call(fetchApi, options);
if (onSuccess)
yield put({
type: onSuccess,
payload: response.data,
});
} catch (error) {
if (onError) yield put({ type: onError, payload: error });
}
}
function* watchApi() {
yield takeEvery(actions.sagaApiCallBegan.type, api);
}
export default function* tweetSagas() {
yield fork(watchApi);
}
I need to export result of axios response on module.exports.
This is my codes:
brand.js
var axios = require('axios');
module.exports = (async function() {
try {
const { data } = axios.get('http://localhost:8000/api/v1/setting/index');
console.log(data.data.initial);
return {
name: data.data.name,
desc: data.data.description,
};
} catch (err) {
console.log(err);
}
})();
I try to import the result to be used on another file.
This is my code.
import React from 'react';
const {brand} = await require("brand.js");
class Dashboard extends Component {
render(){
const name = brand.name
const desc = brand.description;
return (
<h1>{title} | {description}</h1>
);
}
}
The result of my code is:
Can not use keyword 'await' outside an async function
This is error shown on browser:
How to solved this?
you can do like this.
// brand.js
import axios from 'axios';
export const fetchData = async () => {
let response;
try {
response = await axios.get(url, config);
} catch (e) {
// catch error
throw new Error(e.message)
}
// if success return value
return response?.data ? response?.data : null // or set initial value
}
then in your React
import { fetchData } from './path/to/fetchData';
const response = fetchData();
const MyComponent = (props) => {
return (
<div>name: {response.data.name} | desc: {response.data.description}</div>
)
}
Tell me please why i can't to get local data from json in axios.
db.json is at the root of the project, but in the getEvents function it throws error 404.
Help me please
operation.js
import FetchClient from 'app/utils/FetchClient';
import IdsAndByIds from 'app/utils/IdsAndByIds';
import { eventsFetch, setEvents } from './actions';
export const getEvents = () => async (dispatch) => {
try {
const { data } = await FetchClient.get('./db.json');
dispatch(setEvents(IdsAndByIds(data)));
dispatch(eventsFetch(false));
} catch (error) {
console.log(error);
}
};
FetchClient.js
import axios from 'axios';
import { URL_API } from 'app/config'; //localhost:3009
const FetchClient = () => {
const defaultOptions = {
baseURL: URL_API,
method: 'get',
headers: {
'Content-Type': 'application/json'
}
};
const instance = axios.create(defaultOptions);
return instance;
};
export default FetchClient();
actions.js
import * as types from './types';
export const eventsFetch = value => ({
type: types.FETCHING_EVENTS,
payload: value
});
export const setEvents = ({ objById, arrayIds }) => ({
type: types.SET_EVENTS,
payload: {
eventById: objById,
eventsOrder: arrayIds
}
});
I am trying to make an API request using Axios in React-Redux environment. On the console everything seems to be fine, however if I try to access any of the data I either get undefined or empty array.
This is my component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { discoverMovie } from '../actions'
//Home component
class Home extends Component {
//make request before the render method is invoked
componentWillMount(){
this.props.discoverMovie();
}
//render
render() {
console.log('movie res ',this.props.movies.movies.res);
console.log('movie ',this.props.movies);
return (
<div>
Home
movie
</div>
)
}
};
const mapStateToProps = (state) => {
return{
movies : state.movies
}
}
export default connect(mapStateToProps, { discoverMovie })(Home);
This is my action
import { DISCOVER_MOVIE } from '../constants';
import axios from 'axios';
//fetch movie
const fetchMovie = () => {
const url = 'https://api.themoviedb.org/3/discover/movie?year=2018&primary_release_year=2018&page=1&include_video=false&include_adult=false&sort_by=vote_average.desc&language=en-US&api_key=72049b7019c79f226fad8eec6e1ee889';
let result = {
res : [],
status : ''
};
//make a get request to get the movies
axios.get(url).
then((res) => {
result.res = res.data.results;
result.status = res.status;
return result;
});
//return the result after the request
return result;
}
//main action
const discoverMovie = () =>{
const result = fetchMovie();
//return the action
return {
type : DISCOVER_MOVIE,
payload : result
}
}
export default discoverMovie;
This is the reducer
import { DISCOVER_MOVIE } from '../constants';
//initial state
const initialState = {
movies : {},
query : '',
};
//export module
export default (state = initialState, actions) =>{
switch(actions.type){
case DISCOVER_MOVIE :
return {
...state,
movies : actions.payload
};
default :
return state;
}
}
this is the log that I get from the console
as you can see if I log the entire object I see all data, however if go deep and try to access the result I either get an undefined or an empty array and using redux-dev-tools I noticed that the state does not contain any value.
I read on internet including this portal similar issue but could not find any solution for my issue.
Solution
From official docs:
You may use a dedicated status field in your actions
Basically you need to dispatch action for each state to make an async action to work properly.
const searchQuery = () => {
return dispatch => {
dispatch({
type : 'START',
})
//make a get request to get the movies
axios.get(url)
.then((res) => {
dispatch({type : 'PASS', payload : res.data});
})
.catch((err) => {
dispatch({type : 'FAILED', payload : res.error});
});
}
With redux-thunk it's pretty simple to set up. You just have to make some changes to your store. Out the box, I'm pretty sure redux isn't the most friendly with async and that's why thunk is there.
import { ..., applyMiddleware } from "redux";
import thunk from "redux-thunk";
...
const store = createStore(reducer, applyMiddleware(thunk));
...
Then in your action you'll need to return dispatch which will handle your logic for your axios call.
const fetchMovie = () => {
return dispatch => {
const url = //Your url string here;
axios.get(url).then(res => {
dispatch(discoverMovie(res.data.results, res.status);
}).catch(err => {
//handle error if you want
});
};
};
export const discoverMovie = (results, status) => {
return {
type: DISCOVER_MOVIE,
payload: results,
status: status
};
};
Your reducer looks fine, though with the way my code is typed you'll have status separately. You can combine them into it's own object before returning in discoverMovie, if you need status with the results.
This is my first answer on stack so let me know if I can clarify anything better!
When I try to access to the response object in my component it doesnt throw me an error but it wont either print. I do get access to the response in the component but thats it, i cant actually print something.
Actions File
import axios from 'axios';
import { FETCH_USERS, FETCH_USER } from './types';
const BASE_URL = "http://API_URL/endpoint/"
export function fetchUsers(id,first_name, last_name, dob) {
const request = axios.post(`${BASE_URL}member-search?patientId=${id}&firstName=${first_name}&lastName=${last_name}&dateOfBirth=${dob}&length=10`).then(response => { return response; })
return {
type: FETCH_USERS,
payload: request
};
}
export function fetchUser(id) {
const request = axios.get(`${BASE_URL}members/${id}/summary/demographics`).then(response => { return response; })
return{
type: FETCH_USER,
payload: request
};
}
My reducer file
import _ from 'lodash';
import {
FETCH_USERS, FETCH_USER
} from '../actions/types';
export default function(state = [], action) {
switch (action.type) {
case FETCH_USER:
return { ...state, [action.payload.data.member.id]: action.payload.data.member };
// return [ action.payload.data.member, ...state ];
case FETCH_USERS:
return _.mapKeys(action.payload.data.searchResults, 'id');
}
return state;
}
And finally my component where Im trying to render some results of the response.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchUser } from '../actions';
class PatientWrapper extends Component{
componentDidMount() {
const { id } = this.props.match.params;
this.props.fetchUser(id);
}
render(){
const { user } = this.props;
console.log('this.props response: ',user);
if(!user){
return <div>loading...</div>;
}
return(
<div>
Name: {user.firstName}
Last Name: {user.lastName}
</div>
)
}
}
function mapStateToProps({ users }, ownProps) {
// return { users };
return { user: users[ownProps.match.params.id] };
}
export default connect (mapStateToProps, { fetchUser })(PatientWrapper);
I uploaded a Screenshot img of the response : http://prntscr.com/fbs531
What is wrong with my code?
The issue is that in fetchUser action you use a Promise and return it in payload field. This promise does not contain any information you need like response data. So to fix the issue you need to dispatch action only when response is retrieved (e.g. in then success callback).
To implement it you need to pass mapDispatchToProps in the second argument in connect function for your component and pass dispatch function to your action:
function mapDispatchToProps(dispatch) {
return {
fetchUser: id => fetchUser(id, dispatch)
}
}
Then in the action just do the following
function fetchUser(id, dispatch) {
const request = axios.get(`${BASE_URL}/${id}`)
.then(response => dispatch({
type:FETCH_USER,
payload: response
}));
}
For complete example see JSFiddle