I am trying to call a api continuously at 3 seconds interval.I am trying with async and setInterval but not working at all.
Let's look at my code below
component.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { fetchMarketCap } from '../Actions/Marketcap';
class Header extends Component{
componentDidMount(){
setInterval(this.props.fetchMarketCap(), 3000);
}
render(){
const marketcap = this.props.marketcap.map(coin => (
<div key={coin.CoinInfo.Id}>
<h5>{coin.CoinInfo.Name}</h5>
<h5>{coin.RAW.USD.CHANGE24HOUR}</h5>
</div>
));
return (
<div>
{marketcap}
</div>
);
}
}
const mapStateToProps = state => ({
marketcap: state.marketcap.coins
});
export default connect ( mapStateToProps, { fetchMarketCap } )(Header);
and expected action file Marketcap.js
import { FETCH_MARKET_CAP, FETCH_MARKET_CAP_SUCCEED, FETCH_MARKET_CAP_FAILED } from './Types';
export const fetchMarketCap = async () => async dispatch => {
const res = await fetch('https://min-api.cryptocompare.com/data/top/mktcapfull?limit=10&tsym=USD&api_key=46e898b0b5d0319ab6fb94aae5ed2f1a388ff650bffefa1f32f5af1479766b4f');
const response = await res.json()
.then( marketcaps =>
dispatch({
type: FETCH_MARKET_CAP_SUCCEED,
payload: marketcaps.Data
})
)
}
but in console SyntaxError: Unexpected token (10:52) in Marketcap.js.Unexpected token (10:52)
You may need an appropriate loader to handle this file type. in the console.How can i solve it?
One obvious thing is the use of the async keyword... Get rid of the first async like so:
export const fetchMarketCap = () => async dispatch => {
const res = await fetch('https://min-api.cryptocompare.com/data/top/mktcapfull?limit=10&tsym=USD&api_key=46e898b0b5d0319ab6fb94aae5ed2f1a388ff650bffefa1f32f5af1479766b4f');
const response = await res.json()
.then( marketcaps =>
dispatch({
type: FETCH_MARKET_CAP_SUCCEED,
payload: marketcaps.Data
})
)
}
The way I've done this in the past in by using Redux-Sagas middleware. It lets you poll every x seconds, and allows you to start and stop the polling using actions.
See this answer for more details: https://stackoverflow.com/a/52422831/6640093
Related
API
import axios from "axios"
const url = "http://localhost:5000/posts"
export const fetchPosts = () => async () => await axios.get(url)
export const createPost = async (post) => await axios.post(url, post)
ACTION
export const fetchPosts = async (dispatch) => {
try {
const {data} = await api.fetchPosts()
dispatch({
type: types.FETCH_POSTS,
payload: data
})
} catch (err) {
console.error(err)
}
}
STORE
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './index'
import thunk from 'redux-thunk'
export default function configureStore() {
return createStore(rootReducer, applyMiddleware(thunk))
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import configureStore from './Redux/store/configureStore'
const store = configureStore();
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
I want to make get request to my posts by axios. There is no problem in post request but I can't get one.
useEffect(() => {
dispatch(fetchPosts())
}, [dispatch])
When I use this method it throws this error :
Error: Actions must be plain objects. Instead, the actual type was: 'Promise'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.
There is no any syntax or import/export mistake.
Why even if I inserted redux thunk to store it throws me this middleware error ?
You need to update fetchPosts return a function
export const fetchPosts = () => async (dispatch) => {
try {
const {data} = await api.fetchPosts()
dispatch({
type: types.FETCH_POSTS,
payload: data
})
} catch (err) {
console.error(err)
}
}
export const fetchPosts = () => async () => await axios.get(url)
the function above returns a promise
try
export const fetchPosts = async () => {
let res = await axios.get(url)
return res;
}
There is a slight problem in your action. Action creators are functions that return actions. You want to return an action (In this case a function) from your action creator.
So your action shoule be:
export const fetchPosts = () => async (dispatch) => {
// Added this part ^^^^^^
try {
const {data} = await api.fetchPosts()
dispatch({
type: types.FETCH_POSTS,
payload: data
})
} catch (err) {
console.error(err)
}
}
Alternatively, you can make this change to your code:
useEffect(() => {
dispatch(fetchPosts)
// Removed the () ^
}, [dispatch])
I'm trying to access data from my API using Redux but when redux tool kit is showing me its an empty array. The api I've populated using postman and the post method seem to work perfectly fine, but attempting to use the get method to access that data it shows an empty array. My DB has the data though. My Data is an array of Object i.e. [ {...} , {...} , {...} ]
API
import axios from "axios";
const url = "http://localhost:5000/info"
export const fetchInfo = () => axios.get(url);
export const createInfo = (newInfo) => axios.post(url, newInfo);
ACTIONS
import * as api from "../api/index.js";
//constants
import { FETCH_ALL, CREATE } from "../constants/actiontypes";
export const getInfo = () => async (dispatch) => {
try {
const { data } = await api.fetchInfo();
console.log(data);
dispatch({ type: FETCH_ALL, payload: data });
} catch (error) {
console.log(error);
}
};
export const createInfo = (info) => async (dispatch) => {
try {
const { data } = await api.createInfo(info);
dispatch({ type: CREATE, payload: data });
} catch (error) {
console.log(error);
}
};
REDUCER
import { FETCH_ALL, CREATE } from "../constants/actiontypes";
export default (infos = [], action) => {
switch (action.type) {
case FETCH_ALL:
return action.payload;
case CREATE:
return [...infos, action.payload];
default:
return infos;
}
};
COMBINE REDUCERS
import {combineReducers} from "redux";
import infos from "./info"
export default combineReducers({infos})
Component I'm trying to to display it in
import React from "react";
//redux
import { useSelector } from "react-redux";
//component
import MovieDetail from "./MovieDetail"
const MovieTitles = () => {
const infos = useSelector((state) => state.infos);
console.log(infos) // shows me empty array
return (
<div>
{infos.map((i) => (
<MovieDetail info={i} />
))}
</div>
);
};
export default MovieTitles;
Is there something else I'm missing which allows to me to access the data?
thanks
I am trying to make use of thunk to make async calls to api, but I am still getting the error :
Unhandled Runtime Error: Actions must be plain objects. Use custom middleware for async actions.
This is my custom _app component:
// to connect redux with react
import { Provider } from 'react-redux';
import { createWrapper } from 'next-redux-wrapper';
import { createStore, applyMiddleware } from 'redux';
import reducers from '../redux/reducers';
import thunk from 'redux-thunk';
const store = createStore(reducers, applyMiddleware(thunk));
const AppComponent = ({ Component, pageProps }) => {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}
AppComponent.getInitialProps = async (appContext) => {
let pageProps = {};
if (appContext.Component.getInitialProps) {
pageProps = await appContext.Component.getInitialProps(appContext.ctx);
};
return { ...pageProps }
}
// returns a new instance of store everytime its called
const makeStore = () => store;
const wrapper = createWrapper(makeStore);
export default wrapper.withRedux(AppComponent);
And this is the landing page where I am dispatching the action creator:
import { connect } from 'react-redux';
import { fetchPosts } from '../redux/actions';
import { bindActionCreators } from 'redux';
import { useEffect } from 'react';
import Link from 'next/link';
const LandingPage = (props) => {
useEffect(() => {
props.fetchPosts();
}, [props]);
return <div>
<Link href="/">
<a>Home</a>
</Link>
</div>
}
LandingPage.getInitialProps = async ({ store }) => {
store.dispatch(await fetchPosts());
}
const mapDispatchToProps = (dispatch) => {
return {
// so that this can be called directly from client side
fetchPosts: bindActionCreators(fetchPosts, dispatch)
}
}
export default connect(null, mapDispatchToProps)(LandingPage);
Action:
import api from '../../api';
// returning a function and dispatching manually to make use of async await to fetch data
export const fetchPosts = async () => async (dispatch) => {
const response = await api.get('/posts');
dispatch({
type: 'FETCH_POSTS',
payload: response
});
};
Sadly the GitHub Next + Redux example NEXT+REDUX is really complicated for me to understand as I am trying redux for the first time with NextJS.
And every blog post has it's own way of doing it and nothing seems to be working.
I do not want it to make it any more complicated. I would really appreciate if anyone could help me why I am getting this error?
the problem is not with next.js when you calling this :
LandingPage.getInitialProps = async ({ store }) => {
store.dispatch(await fetchPosts());
}
fetchPosts here is a Promise and dispatch dispatch action must be a plain object so to solve this remove async word from it like this :
export const fetchPosts = () => async (dispatch) => {
const response = await api.get('/posts');
dispatch({
type: 'FETCH_POSTS',
payload: response
});
};
butt if you want to wait for api response instead you need call it in the component like this :
const App= ()=>{
const dispatch = useDispatch()
useEffect(() => {
const fetch = async()=>{
try{
const response = await api.get('/posts');
dispatch({
type: 'FETCH_POSTS',
payload: response
});
}
catch(error){
throw error
}
}
fetch()
}, []);
return ....
}
I am pretty sure i am returning an object and have used asyn and await on the promise within my action file. but this still keeps returing the error redux.js:205 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions
https://codesandbox.io/s/frosty-nash-wdcjf?fontsize=14
my action file is returning an object
import axios from "axios";
export const LOAD_URL_STATUS = "LOAD_URL_STATUS";
export async function loadUrlStatus(url) {
const request = await axios
.get(url)
.then(response => {
console.log(response.status);
return response.status;
})
.catch(error => {
console.log("Looks like there was a problem: \n", error);
});
console.log(request);
console.log(LOAD_URL_STATUS);
return {
type: LOAD_URL_STATUS,
payload: request
};
}
it fails when calling this action in componenDidMount this.props.loadUrlStatus(url);
component
import React from 'react';
import TrafficLight from '../TrafficLight';
import {connect} from 'react-redux';
import {loadUrlStatus} from "../../actions";
//import {withPolling} from "../Polling";
//import Polling from "../Polling/polling";
import { bindActionCreators } from 'redux';
class TrafficLightContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
redOn: true,
yellowOn: false,
greenOn: false,
}
}
componentDidMount(){
console.log("componentDidMount")
const {pollingAction, duration, url} = this.props
//withPolling(this.props.loadUrlStatus(this.props.url),1)
/*
const {pollingAction, duration, url} = this.props
this.dataPolling = setInterval(
() => {
this.props.loadUrlStatus(url);
},
10000);
*/
this.props.loadUrlStatus(url);
};
componentWillUnmount() {
clearInterval(this.dataPolling);
}
render() {
console.log(this.props)
return (
<TrafficLight
Size={100}
onRedClick={() => this.setState({ redOn: !this.state.redOn })}
onGreenClick={() => this.setState({ greenOn: !this.state.greenOn })}
RedOn={this.state.redOn}
GreenOn={this.state.greenOn}
/>
)
}
}
const mapStateToProps = state => ({
...state
});
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
loadUrlStatus
},
dispatch
);
};
export default (
connect(mapStateToProps, mapDispatchToProps)(TrafficLightContainer));
index
import React from 'react';
import { render } from 'react-dom'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import configureStore from './configureStore'
const store = configureStore();
const renderApp = () =>
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
if (process.env.NODE_ENV !== 'production' && module.hot) {
module.hot.accept('./App', renderApp)
}
renderApp();
serviceWorker.unregister();
The problem is that loadUrlStatus is async function, so it returns not object, but Promise, and object inside it promise.
To correct this, modify loadUrlStatus so it return another function. As you already applied thunk middleware during store creation, such function will be called inside redux. (You can see samples of async functions here)
export function loadUrlStatus(url) {
// Immediately return another function, which will accept dispatch as first argument. It will be called inside Redux by thunk middleware
return async function (dispatch) {
const request = await axios
.get(url)
.then(response => {
console.log(response.status);
return response.status;
})
.catch(error => {
console.log("Looks like there was a problem: \n", error);
});
console.log(request);
console.log(LOAD_URL_STATUS);
dispatch ({
type: LOAD_URL_STATUS,
payload: request
});
}
}
If you're using await in an action creator, you'll want to return a function from the action creator. Otherwise, return on object. A library like redux-thunk will help you do just that.
Your action creator would then look like this:
import axios from "axios";
export const LOAD_URL_STATUS = "LOAD_URL_STATUS";
export const loadUrlStatus(url) => async dispatch => {
try {
const response = await axios(url)
dispatch({
type: LOAD_URL_STATUS,
payload: response.status
})
} catch (error) {
// dispatch error
}
}
I am just learning react-redux and trying to fire a thunk, this is the thunk:
const getRepos = dispatch => {
try {
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
fetch(url)
.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
});
} catch (error) {
console.error(error);
}
};
I hooked up my component to the store:
const bla = dispatch =>
bindActionCreators(
{
geklikt,
getRepos
},
dispatch
);
const Container = connect(
null,
bla
)(Dumb);
When I trigger the getRepos thunk I get:
Actions must be plain objects. Use custom middleware for async
actions.
What could be the issue? I included the middleware? link to code
sandbox
Please refactor your application structure, it's all in one file and extremely hard to read.
Things to consider:
Use a switch statement in your reducers
Separate components from containers: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
Make sure to set initial reducer state: state={}, state=[] ...etc.
Simplify the action to either use .then().catch() or use async/await within a try/catch block.
In the meantime, here's a working version: https://codesandbox.io/s/oxwm5m1po5
actions/index.js
import { GEKLIKT } from "../types";
export const getRepos = () => dispatch =>
fetch(`https://api.github.com/users/reduxjs/repos?sort=updated`)
.then(res => res.json())
.then(data => dispatch({ type: GEKLIKT, payload: data }))
.catch(err => console.error(err.toString()));
/*
export const getRepos = () => async dispatch => {
try {
const res = await fetch(`https://api.github.com/users/reduxjs/repos?sort=updated`)
const data = await res.json();
dispatch({ type: GEKLIKT, payload: data }))
} catch (err) { console.error(err.toString())}
}
*/
components/App.js
import React from "react";
import Dumb from "../containers/Dumb";
export default () => (
<div className="App">
<Dumb />
</div>
);
containers/Dumb.js
import React from "react";
import { connect } from "react-redux";
import { getRepos } from "../actions";
let Dumb = ({ data, getRepos }) => (
<div>
hi there from Dumb
<button onClick={getRepos}>hier</button>
<pre>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
</div>
);
export default connect(
state => ({ data: state.data }),
{ getRepos }
)(Dumb);
reducers/index.js
import { combineReducers } from "redux";
import { GEKLIKT } from "../types";
const klikReducer = (state = {}, { payload, type }) => {
switch (type) {
case GEKLIKT:
return { ...state, data: payload };
default:
return state;
}
};
export default combineReducers({
data: klikReducer
});
root/index.js
import React from "react";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";
import App from "../components/App";
const store = createStore(rootReducer, applyMiddleware(thunk));
export default () => (
<Provider store={store}>
<App />
</Provider>
);
types/index.js
export const GEKLIKT = "GEKILKT";
index.js
import React from "react";
import { render } from "react-dom";
import App from "./root";
import "./index.css";
render(<App />, document.getElementById("root"));
You returned the promise in action. A promise is not a plain object and so the returned action would not be a plain object and hence the error.
Since you're using the thunk middleware your actions can be functions and here's how you'd do it.
const GET_REPOS_REQUEST = "GET_REPOS_REQUEST";
const GET_REPOS_SUCCESS = "GET_REPOS_SUCCESS";
const GET_REPOS_ERROR = "GET_REPOS_ERROR";
export function getRepos() {
return function action(dispatch) {
dispatch({type: GET_REPOS})
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
const request = fetch(url);
return request.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
dispatch({type: GET_REPOS_SUCCESS, json});
})
.then(err => {
dispatch({type: GET_REPOS_ERROR, err});
console.log(“error”, err);
});
};
}
Arrow function way:
export getRepos = () =>{
return action = dispatch => {
dispatch({type: GET_REPOS})
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
const request = fetch(url);
return request.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
dispatch({type: GET_REPOS_SUCCESS, json});
})
.then(err => {
console.log(“error”, err);
dispatch({type: GET_REPOS_ERROR, err});
});
};}