I am using API call via Async-Await that requires a custom middleware, I am using Thunk as custom Middleware. All dependencies are installed. Then also, somehow it fails to detect the middleware or something like that.
My code when executed shows the following error:
This is my Actions Page: I used Async-Await which led me to this error
import axios from "axios";
export const saveTwitterToken = (token) => {
return ({
type: TWITTER_OAUTH,
payload: token
});
};
export const callTwitterAPI = async (config) => {
const request = await axios({
"url": "https://api.twitter.com/1.1/statuses/home_timeline.json",
"method": "GET",
"headers": {
"Authorization": " _Something_ "
}
});
return ({
type: "api",
payload: request
})
};
This is my Index.js Page:
import React from "react";
import ReactDOM from "react-dom";
import App from './App'
import {Provider} from "react-redux";
import 'bootstrap/dist/css/bootstrap.css';
import stores from "./utils/store";
ReactDOM.render(
<Provider store={stores}>
<App/>
</Provider>
, document.getElementById('root'));
This is the Custom Middleware Store File:
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers';
const store = createStore(
reducers,
{},
compose(
applyMiddleware(thunk)
)
);
export default store;
You've to use dispatch for Asynchronous Redux Action as follow:
export const callTwitterAPI = config => async dispatch => {
const request = await axios({
"url": "https://api.twitter.com/1.1/statuses/home_timeline.json",
"method": "GET",
"headers": {
"Authorization": " _Something_ "
}
});
dispatch({
type: "api",
payload: request
})
}
More better way is to use promise as follow:
export const callTwitterAPI = config => dispatch => {
axios({
"url": "https://api.twitter.com/1.1/statuses/home_timeline.json",
"method": "GET",
"headers": {
"Authorization": " _Something_ "
}
}).then(request =>
dispatch({
type: "api",
payload: request
})
).catch(error => handle it OR dispatch another action)
}
I hope it help you.
Related
I am working on a signup React app that uses redux. Everything other thing works quite right with the exception of state update.
I've gone through several recommendations already given here and I don't seem to see what's wrong with the code.
The authAction.js
import { API_URL } from '../../../constants/constants';
const LOGIN_SUCCESSFUL = 'LOGIN_SUCCESSFUL';
const LOGIN_LOADING = 'LOGIN_LOADING';
const LOGIN_FAILED = 'LOGIN_FAILED';
const login = values => {
let url = API_URL + 'login';
return async (dispatch) => {
dispatch({
type: LOGIN_LOADING
})
const response = await fetch (url, {
method: 'POST',
body: JSON.stringify(values),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log(data);
if(response.status >=200 && response.status <= 299)
{
sessionStorage.setItem('_token', data.data.jwt)
dispatch({
type: LOGIN_SUCCESSFUL,
payload: {
isAuthenticated: true,
jwt: data.data.jwt ?? ''
}
});
}
dispatch({
type: LOGIN_FAILED,
payload: {
isAuthenticated: false,
jwt: '',
message: data?.message ?? 'Authentication failed.'
}
})
}
}
export { login, logout };
authReducer.js
const LOGIN_SUCCESSFUL = 'LOGIN_SUCCESSFUL';
const LOGIN_FAILED = 'LOGIN_FAILED';
const LOGIN_LOADING = 'LOGIN_LOADING';
const initialState = {
jwt: '',
isAuthenticated: false,
message: '',
loading: false,
error: false,
};
const authReducer = (state = initialState, action) => {
if(action.type === LOGIN_LOADING)
{
return {
...state,
message: 'Authenticating...',
loading: true
}
}
if(action.type === LOGIN_SUCCESSFUL)
{
return {
...state,
isAuthenticated: true,
jwt: action.payload.jwt,
message: action.payload.message,
laoding: false,
error: true
}
}
if(action.type === LOGIN_FAILED)
{
return {
...state,
jwt: '',
isAuthenticated: false,
loading: false
};
}
return initialState;
}
export default authReducer;
rootReducer.js where I combined other reducers
import { combineReducers } from "redux";
import userReducer from "./users/userReducer";
import authReducer from './users/authReducer';
import signupReducer from './users/signupReducer';
import postReducer from './postReducer'
const rootReducer = combineReducers({
user: userReducer,
auth: authReducer,
signup: signupReducer,
posts: postReducer
});
export default rootReducer;
signup.js that handles the view
import {useFormik } from 'formik';
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { Link, Navigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import * as Yup from 'yup';
import logo from '../../assets/img/logo1.jpeg';
import Error from '../../components/forms/Error';
import LandingLayout from '../layouts/landing';
import signup from '../../redux/actions/users/signupActions';
import Toast from '../../components/alerts/Toast';
const Signup = () => {
const {loading, error, status} = useSelector(state => state.signup);
const dispatch = useDispatch();
useEffect(()=>{
if(status)
{
setTimeout(() => {
return <Navigate to='/login' />
}, 2000);
}
}, [dispatch, status])
...
onSubmit: (values) => {
dispatch(signup(values));
}
...
export default Signup;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import rootReducer from './redux/reducers/rootReducer';
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
ReactDOM.render(
<React.StrictMode>
<Provider store = {store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
when a log the response from the API call, I get the expected response but nothing is effected on the UI.
Well, it appears that the error was coming from somewhere else. Just as previously stated, everything I did was quite right aside the fact that one of the reducers - userReducer - included in the rootReducer had its action creator returning the wrong payload.
I commented that out and everything else worked.
However, should subtle bug from one reducer affect the entire workings of the store?
I am using saga.js with Redux in my project and I am trying to call an API but that API is not being called. The generator function is called, but with yield.put() other method is not being called. I am fairly new to Redux Saga and I am stuck here. Any help would be really appreciated.
Saga.js
import { put, takeEvery, all ,fork, takeLatest} from "redux-saga/effects";
import axios from "axios";
function* runOurAction() {
let remoteData;
yield axios.get(url).then((resp) => {
remoteData = resp.data;
});
yield put({ type: "SET_DATA", payload: remoteData });
};
function* getAsyncDataWatcher() {
yield takeLatest("GET_TEAMS", runOurAction);
}
export default function* rootSaga() {
yield fork(getAsyncDataWatcher)
}
getAsyncDataWatcher() is being called but its not calling runOurAction
Reducer.js
const teams=(state=[],action)=>{
switch(action.type) {
case "SAVE_TEAMS":
return { ...state, payload: action.payload };
case "GET_TEAMS":
return { ...state, payload: action.payload };
case "SET_DATA":
return { ...state, payload: action.payload };
default:
return state;
}
}
export default teams;
Actions.js
const getTeams = (payload) => {
return {
type: "GET_TEAMS",
payload:payload
};
};
const saveTeams = (payload) => {
return {
type: "SAVE_TEAMS",
payload:payload
};
};
export { saveTeams, getTeams };
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import rootReducer from "./reducers";
import createSagaMiddleware from "redux-saga";
import { createStore, applyMiddleware ,compose} from "redux";
import { Provider } from "react-redux";
import rootSaga from "./saga";
const sagaMiddleware = createSagaMiddleware();
const enhancers = [window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), applyMiddleware(sagaMiddleware)];
const store = createStore(
rootReducer,
compose(...enhancers)
);
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
//
serviceWorker.unregister();
Team.js
import React,{useEffect} from 'react'
import {useSelector,useDispatch} from "react-redux";
import {getTeams} from "../actions";
const Team=()=> {
const data=useSelector(state=>state.teams);
const dispatch=useDispatch();
useEffect(() => {
console.log("Called");
dispatch(getTeams());
}, [dispatch])
console.log("hello",data);
return (
<div>
</div>
)
}
export default Team
The problem is probably caused by axios.get() as the return is a promise. Try the following:
yield axios.get(url).then((resp) => {
return resp.data;
}).then(response => {
remoteData = response;
});
I think you can't simply call axios function directly that way. You have to wrap it in call a saga-effects which only takes a function as its argument not a Promise resolve, so it would look like:
import { call } from 'redux-saga/effects';
// Wrap in a call which takes a function as argument
const remoteData = yield call(() => axios.get(url).then(response => repsonse.data));
// Or you can simply write in shorter way
const { data: remoteData } = yield call(axios.get, url);
I'm getting the not a function error on addExperience() despite the fact that the function I'm using is defined as such. It's modeled off of other functions that work just fine. At the same time, I'm also getting the "defined but never used" warning.
addExperience.js:
import React, {Fragment, useState} from "react";
import {Link, withRouter} from "react-router-dom";
import PropTypes from "prop-types";
import {connect} from "react-redux";
import {addExperience} from "../../actions/profile";
...
const onSubmit = (e) => {
e.preventDefault();
addExperience(formData, history);
};
...
actions/profile.js:
import axios from "axios";
import {setAlert} from "./alert";
import {GET_PROFILE, PROFILE_ERROR, UPDATE_PROFILE} from "./types";
...
export const addExperience = (formData, history) => async (dispatch) => {
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const res = await axios.post("/api/profile/experience", formData, config);
dispatch({
type: UPDATE_PROFILE,
payload: res.data,
});
dispatch(setAlert("Experience Added", "success"));
history.push("/dashboard");
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: PROFILE_ERROR,
payload: {msg: err.response.statusText, status: err.response.status},
});
}
};
This issue likely stems from a misconfiguration of redux-thunk or a misunderstanding of how to write a thunk. I've tried a lot of different ways, but from what I can tell, this should work. However, I'm still getting a console message that says its firing a redux action of undefined.
Here is my store configuration
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import App from './components/App';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('rootElement')
);
Here is my action:
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState.isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState.about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Here is me firing the action in my component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../../actions/about';
import getAboutContent from '../../reducers';
class AboutMe extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.getAboutContent();
}
render() {
return <div>{ this.props.content }</div>
}
}
const mapStateToProps = (state) => ({
content: {} || getAboutContent(state)
})
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ getAboutContent }, dispatch)
export default connect(
mapStateToProps, mapDispatchToProps
)(AboutMe);
I've tried quite a few configurations for mapDispatchToProps, i.e. connect(..., { fetchData: getAboutContent })..., and more. Any help is greatly appreciated.
Edit:
Here is the git repo, if that is helpful to anybody: https://github.com/sambigelow44/portfolio-page
Check your reducer name,you export fetchAboutContent, but import getAboutContent.
Code in action file is seems to be incorrect.
getState is a function.
const state = getState();
Change below code.
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState().isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState().about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Also you need to return promise from axios call, just add return statement.
return axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
I'm actually trying to use redux-promise, and when i send a request to my api in my action, i get this error.
I saw that there was a speaking subject to the same error but I have not found my problem solving in responses.
So this is my code :
./app.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import { Router, browserHistory } from 'react-router'
import reducers from './reducers/app-reducer'
import routes from './routes'
import promise from 'redux-promise'
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>, document.querySelector('.container'))
./reducers/app-reducer.js
import { combineReducers } from 'redux'
import user from './user-reducer'
const rootReducer = combineReducers({
user: user
})
export default rootReducer
action whose called :
./actions/user-actions.js
import axios from 'axios'
export const UPDATE_TOKEN = 'UPDATE_TOKEN'
export const CREATE_SESSION = 'CREATE_SESSION'
export const CREATE_SESSION_ERROR = 'CREATE_SESSION_ERROR'
export function createSession(obj){
if (typeof obj === 'string') {
return {
type: UPDATE_TOKEN,
payload: obj
}
}
else {
axios.post('http://localhost:8080' + '/session', {
data: {'Email': obj.email, 'Password': obj.password},
headers: {'Content-Type': 'application/json'}
}).then((response) => {
return {
type: CREATE_SESSION,
payload: response.data
}
}).catch((error) => {
return {
type: CREATE_SESSION_ERROR,
payload: error
}
})
}
}
I've also tried with redux-thunk, I get the same error.
Someone you have an idea ? or maybe do I take me wrong?
Thanks
When creating ajax calls in actions, you should use redux-thunk and compose.
import {createStore, applyMiddleware, compose} from "redux";
import thunk from 'redux-thunk';
Change your store like this:
const createStoreWithMiddleware = compose(applyMiddleware(thunk))(createStore)(reducers);
And set axios to use dispatch:
return (dispatch) => {
axios.post('http://localhost:8080' + '/session', {
data: {'Email': obj.email, 'Password': obj.password},
headers: {'Content-Type': 'application/json'}
}).then((response) => {
dispatch ({
type: CREATE_SESSION,
payload: response.data
})
}).catch((error) => {
return {
type: CREATE_SESSION_ERROR,
payload: error
}
})
}