When I send a GET request from my ReactJS frontend, It recieves an error even though my django backend shows [10/Mar/2018 23:31:08] "GET /post/ HTTP/1.1" 200 930 in the terminal.
I'm using redux sagas.
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
import rootReducer from './reducers';
import PostsIndex from './components';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={PostsIndex}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('.container'));
src/components/index.js
import React, { Component } from 'react';
import { fetchPosts } from '../actions';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import _ from 'lodash';
class PostsIndex extends Component {
componentDidMount() {
this.props.fetchPosts();
}
renderPosts() {
return _.map(this.props.posts, (post) => {
return(
<li className="list-group-item" key={post.id}>
{post.text}
</li>
)
})
}
render() {
const { posts } = this.props
if (posts.isLoading) {
return (
<div>
<h3>Loading...</h3>
</div>
)
} else if (posts.error) {
return (
<div>
<h3>Error getting posts</h3>
<h2>{JSON.stringify(posts.error)}</h2>
</div>
)
} else {
return (
<div>
<div className="text-xs-right">
<Link className="btn btn-primary" to="/posts/new">
Add a Post
</Link>
</div>
<h3>Posts</h3>
<ul className="list-group">
{this.renderPosts()}
</ul>
</div>
);
}
}
}
function mapStateToProps({ posts }){
return { posts }
}
export default connect(mapStateToProps, { fetchPosts })(PostsIndex);
src/actions/index.js
export const FETCH_POSTS = 'fetch_posts';
export function fetchPosts() {
return { type: FETCH_POSTS };
}
src/api/index.js
import axios from 'axios';
const ROOT_URL = 'http://127.0.0.1:8000';
export function fetch(endpoint) {
return axios.get(`${ROOT_URL}/${endpoint}/`)
.then(response => ({ response }))
.catch(error => ({ error }));
}
src/reducers/index.js
import { combineReducers } from 'redux';
import { POSTS_REQUESTED, POSTS_RECEIVED, POSTS_REQUEST_FAILED } from '../sagas';
import _ from 'lodash';
function postsReducer(state = {}, action) {
switch(action.type) {
case POSTS_REQUESTED:
return {
content: null,
isLoading: true,
error: null
}
case POSTS_RECEIVED:
return {
content: _.mapKeys(action.posts, 'id'),
isLoading: false,
error: null
}
case POSTS_REQUEST_FAILED:
return {
content: null,
isLoading: false,
error: action.error
}
default:
return state;
}
}
const rootReducer = combineReducers({
posts: postsReducer,
});
export default rootReducer;
src/sagas/index.js
import { call, put, takeEvery } from 'redux-saga/effects'
import { fetch } from '../api';
import { FETCH_POSTS } from '../actions';
export const POSTS_REQUESTED = 'posts_requested';
export const POSTS_RECEIVED = 'posts_recieved';
export const POSTS_REQUEST_FAILED = 'posts_request_failed';
export function* fetchPosts() {
yield put({ type: POSTS_REQUESTED })
const { response, error} = yield call(fetch, 'post');
if (response)
yield put({ type: POSTS_RECEIVED, posts: response })
else
yield put({ type: POSTS_REQUEST_FAILED, error })
}
export function* watchFetchPosts() {
yield takeEvery(FETCH_POSTS, fetchPosts);
}
// single entry point to start all Sagas at once
export default function* rootSaga() {
yield [
watchFetchPosts()
]
}
The output I get is
Error getting posts
{"config":{"transformRequest":{},"transformResponse":{},"timeout":0,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","maxContentLength":-1,"headers":{"Accept":"application/json,
text/plain,
/"},"method":"get","url":"http://127.0.0.1:8000/post/"},"request":{}}
When I copy paste that url into my browser, it works perfectly, so it has to be a problem with the frontend.
Why am I getting this error?
Turns out the problem was related to this
Since I'm using django-rest, I just had to add django-core-headers to my backend like in this answer
Related
I'm new to redux and trying to fetch content from my BackEnd API. For some reason the action I call does not reach the reducer (It's not even executed). I first thought it was because it couldn't access the store since it is has a parent component but my Provider is well configured and there is another component at the same level, and just after i started thinking it was a problem with my dispatch but honestly i don't know. I have attached the code I feel is relevant and any contributions would be highly appreciated.
actions/viewers.js
import axios from 'axios';
import { VIEWERS_LOADED, VIEWERS_ERROR } from './types';
export const loadData = async (body, http) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
try {
const res = await axios.post(
http,
body,
config
);
return res.data;
} catch (error) {
console.log(error);
}
};
export const extractConcurrentViewers = (from, to, aggregate) => async dispatch => {
console.log("CONCURRENT VIEWERS");
const body = {
session_token: localStorage.token,
from,
to,
};
try {
let aggregateConcur = null;
const graphConccur = await loadData(body, 'http://localhost:5000/audience');
console.log('extractViews -> res_1', graphConccur);
if (aggregate !== null) {
body.aggregate = aggregate
aggregateConcur = await loadData(body, 'http://localhost:5000/audience');
}
console.log('extractaggregateViewers -> res_2', aggregateConcur);
dispatch({
type: VIEWERS_LOADED,
payload: {
graphConccur,
aggregateConcur
},
});
} catch (error) {
console.log(error);
dispatch({
type: VIEWERS_ERROR,
});
}
}
reducers/viewers.js
import {
VIEWERS_LOADED,
VIEWERS_ERROR,
} from '../actions/types';
const initialState = {
session_token: localStorage.getItem('token'),
concurrence: null,
aggConcurrence: null,
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case VIEWERS_LOADED:
return {
...state,
...payload,
concurrence: payload.graphConccur.audience,
aggConcurrence: payload.aggregateConcur.audience,
};
case VIEWERS_ERROR:
return {
...state,
concurrence: null,
aggConcurrence: null,
};
default:
return state;
}
}
reducer/index.js
import {combineReducers} from 'redux';
import alert from './alert';
import auth from './auth'
import profile from './profile'
import chart from './chart'
import viewers from './viewers'
export default combineReducers({
alert,
auth,
profile,
chart,
viewers
});
App.js
import React, { useEffect } from 'react';
import Navbar from './components/layout/Navbar';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Landing from './components/layout/Landing';
import Login from './components/auth/Login';
import Register from './components/auth/Register';
import Alert from './components/layout/Alert';
import Dashboard from './components/dashboard/Dashboard';
import PrivateRoute from './components/routing/PrivateRouting';
import { Provider } from 'react-redux';
import store from './store';
import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken'
import './App.css';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser())
}, []);
return (
<Provider store={store}>
<Router>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/login' component={Login} />
<Route exact path='/register' component={Register} />
<PrivateRoute exact path='/dashboard' component={Dashboard} />
</Switch>
</section>
</Router>
</Provider>
);
};
export default App;
This is where the function extractConcurrentViewers is to be called and the component supposed to use that is <Concurrent concurrence={concurrence}/> and what is really weird about is that the component just above it is implemented almost the same way but it's working.
import React, { useEffect, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import BandWidth from './BandWidth';
import Concurrent from './Concurrent';
import { extractCurrentClient } from '../../actions/profile';
import { extractchartData } from '../../actions/chart';
import { extractConcurrentViewers } from '../../actions/viewers';
const Dashboard = ({
extractCurrentClient,
extractchartData,
auth: { user },
profile: { profile, loading },
chart: { cdn, p2p, maxSum, maxCdn },
viewers: {concurrence}
}) => {
useEffect(() => {
extractCurrentClient();
extractchartData('max', 1585834831000, 1589118031000);
extractConcurrentViewers(1585834831000, 1589118031000);
}, []);
return loading && profile === null ? (
<Spinner />
) : (
<Fragment>
<h1 className='large text-primary'>Streaming</h1>
<p className='lead'>
<i className='fas fa-chart-line'></i>
Welcome {user && user.lname}
</p>
<BandWidth cdn={cdn} p2p={p2p} maxSum={maxSum} maxCdn={maxCdn} />
{/* <Concurrent concurrence={concurrence}/> */}
</Fragment>
);
};
Dashboard.propTypes = {
extractCurrentClient: PropTypes.func.isRequired,
extractchartData: PropTypes.func.isRequired,
extractConcurrentViewers: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
profile: state.profile,
chart: state.chart,
viewers: state.viewers,
});
export default connect(mapStateToProps, {
extractCurrentClient,
extractchartData,
extractConcurrentViewers
})(Dashboard);
store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
You mapped extractConcurrentViewers to props in connect but did not add it to the destructured props object. Since they share the same name, that means is you're calling your action creator without it being bound to dispatch, so it will not be delivered to your reducers.
const Dashboard = ({
extractCurrentClient,
extractchartData,
auth: { user },
profile: { profile, loading },
chart: { cdn, p2p, maxSum, maxCdn },
viewers: {concurrence},
extractConcurrentViewers // <-- add this
}) => {
Personally I don't destructure my props and this is one reason. I prefer the code to be explicit about where values and functions are coming from props.extractConcurrentViewers . But that's my preference.
Need some help.
As I am trying to get some understanding of React/REdux global state I made some simple get request.
This is done with Axios, thunk, Redux, but i can't get this working
I have Post.js file, nothing fancy
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PostForm from './PostForm';
export class Post extends Component {
static propTypes = {
posts: PropTypes.any,
fetchPosts: PropTypes.func,
};
componentDidMount() {
const { fetchPosts } = this.props;
fetchPosts();
}
render() {
const { posts } = this.props;
return (
<div>
<PostForm addPost={this.onSubmit} />
<br />
<div>
{posts.map(post => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
))}
</div>
</div>
);
}
}
export default Post;
Next i have my PostContainer.js
import { connect } from 'react-redux';
import Post from './Post';
import { fetchFromApi } from '../reducers/postReducers';
const mapStateToProps = state => ({
posts: state.posts,
});
const mapDispatchToProps = dispatch => ({
fetchPosts: () => dispatch(fetchFromApi()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Post);
My reducer
import Axios from 'axios';
/* action type */
const FETCH_POSTS = 'FETCH_POSTS';
/* action creator */
export const fetchStarted = payload => ({ payload, type: FETCH_POSTS });
/* thunk */
export const fetchFromApi = () => {
return (dispatch, getState) => {
Axios.get('https://jsonplaceholder.typicode.com/posts?_limit=5').then(res =>
dispatch(fetchStarted(res.data))
);
};
};
/* reducer */
export default function reducer(state = [], action = {}) {
switch (action.type) {
case FETCH_POSTS: {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
and my store
import { combineReducers, applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import postReducer from './reducers/postReducers';
const initialState = {
posts: {
data: {},
},
};
const reducers = {
posts: postReducer,
};
Object.keys(initialState).forEach(item => {
if (typeof reducers[item] == 'undefined') {
reducers[item] = (state = null) => state;
}
});
const combinedReducers = combineReducers(reducers);
const store = createStore(
combinedReducers,
initialState,
composeWithDevTools(applyMiddleware(thunk))
);
export default store;
All of that is doing not much. My map method is trying to map empty posts object. And for some reason my fetchPosts is not dispatched. I have reade some old posts here but still can't get this working
Thanks
Edit
this is my app.js file with container
import React from 'react';
import './App.css';
import Post from './components/PostContainer';
import { Provider } from 'react-redux';
import store from './store';
function App() {
return (
<Provider store={store}>
<div className='App'>
<Post />
</div>
</Provider>
);
}
export default App;
I managed to get this working.
Data was not there when my posts array was render. After passing simple if statemante all is working
I don't know how to load the data of the fetchLatestAnime action in the react app.js file.
My mission is to show the endpoint data that I am doing fetch.
I have already implemented the part of the reducers and action, which you can see in the part below. The only thing I need is to learn how to display the data.
App.js
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
</div>
);
}
export default App;
actions/types.js
export const FETCHING_ANIME_REQUEST = 'FETCHING_ANIME_REQUEST';
export const FETCHING_ANIME_SUCCESS = 'FETCHING_ANIME_SUCCESS';
export const FETCHING_ANIME_FAILURE = 'FETCHING_ANIME_FAILURE';
actions/animesActions.js
import{
FETCHING_ANIME_FAILURE,
FETCHING_ANIME_REQUEST,
FETCHING_ANIME_SUCCESS
} from './types';
import axios from 'axios';
export const fetchingAnimeRequest = () => ({
type: FETCHING_ANIME_REQUEST
});
export const fetchingAnimeSuccess = (json) => ({
type: FETCHING_ANIME_SUCCESS,
payload: json
});
export const fetchingAnimeFailure = (error) => ({
type: FETCHING_ANIME_FAILURE,
payload: error
});
export const fetchLatestAnime = () =>{
return async dispatch =>{
dispatch(fetchingAnimeRequest());
try{
let res = await axios.get('https://animeflv.chrismichael.now.sh/api/v1/latestAnimeAdded');
let json = await res.data;
dispatch(fetchingAnimeSuccess(json));
}catch(error){
dispatch(fetchingAnimeFailure(error));
}
};
};
reducers/latestAnimeReducers.js
import {
FETCHING_ANIME_FAILURE,
FETCHING_ANIME_REQUEST,
FETCHING_ANIME_SUCCESS
} from '../actions/types';
const initialState = {
isFetching: false,
errorMessage: '',
latestAnime: []
};
const latestAnimeReducer = (state = initialState , action) =>{
switch (action.type){
case FETCHING_ANIME_REQUEST:
return{
...state,
isFetching: true,
}
case FETCHING_ANIME_FAILURE:
return{
...state,
isFetching: false,
errorMessage: action.payload
}
case FETCHING_ANIME_SUCCESS:
return{
...state,
isFetching: false,
latestAnime: action.payload
}
default:
return state;
}
};
export default latestAnimeReducer;
reducers/index.js
import latestAnimeReducers from './latestAnimeReducers'
import {combineReducers} from 'redux';
const reducers = combineReducers({
latestAnimeReducers
});
export default reducers;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import resolvers from './redux/reducers/index';
import {createStore , applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import thunk from 'redux-thunk';
const REDUX_DEV_TOOLS = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(resolvers , REDUX_DEV_TOOLS)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
Ideally, this is how your app.js should look like. I created a working codesandbox for you here. Your initial latestAnime state was an empty array but the action payload you set to it is an object, so remember to pass payload.anime like i have done in the sandbox.
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { fetchLatestAnime } from "./redux/actions/animesActions";
const App = props => {
const { fetchLatestAnime, isFetching, latestAnime, errorMessage } = props;
useEffect(() => {
fetchLatestAnime();
}, [fetchLatestAnime]);
console.log(props);
if (isFetching) {
return <p>Loading</p>;
}
if (!isFetching && latestAnime.length === 0) {
return <p>No animes to show</p>;
}
if (!isFetching && errorMessage.length > 0) {
return <p>{errorMessage}</p>;
}
return (
<div>
{latestAnime.map((anime, index) => {
return <p key={index}>{anime.title}</p>;
})}
</div>
);
};
const mapState = state => {
return {
isFetching: state.latestAnimeReducers.isFetching,
latestAnime: state.latestAnimeReducers.latestAnime,
errorMessage: state.latestAnimeReducers.errorMessage
};
};
const mapDispatch = dispatch => {
return {
fetchLatestAnime: () => dispatch(fetchLatestAnime())
};
};
export default connect(
mapState,
mapDispatch
)(App);
I'm trying to dispatch an action, but it returns "type" of undefined. I suspect Redux Thunk is not working properly.
Before I was dispatching the same action from the parent component and it was working.
Entry point
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from '../ConfigureStore'
import '../App.css';
import App from './theapp/theAppContainer';
const store = configureStore()
class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
export default Root;
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import allReducers from './reducers/index'
const loggerMiddleware = createLogger()
export default function configureStore() {
return createStore(
allReducers,
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
The app - routing. Before I was dispatching the action at this level and it was working.
import React, { Component } from 'react'
import Cards from '../templates/cards/CardsContainer'
import EditApp from '../pages/editApp/EditApp'
import NewApp from '../pages/NewApp'
import AppReport from '../pages/AppReport'
import { Route, Switch, HashRouter } from 'react-router-dom'
export default class TheApp extends Component {
constructor(props) {
super(props)
}
render() {
const appId = window.location.href.split('id=')[1];
return (
<HashRouter>
<Switch>
<Route exact path="/" component={Cards} />
<Route path="/app" component={EditApp} />
<Route exact path="/new" component={NewApp} />
<Route path="/report" component={AppReport} />
</Switch>
</HashRouter>
)
}
}
The container where I dispatch the action
import { connect } from 'react-redux'
import Cards from './Cards'
import {
fetchAppsData
} from '../../../actions'
function mapStateToProps(state){
return {
apps: state.apps
}
}
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
export default connect(mapStateToProps, matchDispatchToProps)(Cards)
Action
import fetch from 'cross-fetch'
import * as helpers from '../Helpers';
export const REQUEST_ITEMS = 'REQUEST_ITEMS'
export const RECEIVE_ITEMS = 'RECEIVE_ITEMS'
export function fetchAppsData() {
return (dispatch) => {
return dispatch(fetchItems())
}
}
function fetchItems() {
return dispatch => {
dispatch(requestItems())
return fetch(helpers.appData)
.then(response => response.json())
.then(json => dispatch(receiveItems(json)))
}
}
function requestItems() {
return {
type: REQUEST_ITEMS
}
}
function receiveItems(json) {
return {
type: RECEIVE_ITEMS,
items: json,
receivedAt: Date.now()
}
}
The reducer
import {
REQUEST_ITEMS,
RECEIVE_ITEMS
} from '../actions/apps-actions'
export default function apps(
state = {
isFetching: false,
items: []
},
action
) {
switch (action.type) {
case REQUEST_ITEMS:
return Object.assign({}, state, {
isFetching: true
})
case RECEIVE_ITEMS:
return Object.assign({}, state, {
isFetching: false,
items: action.items
})
default:
return state
}
}
Try changing
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
Into this:
function matchDispatchToProps(dispatch){
return {
fetchAppsData: () => dispatch(fetchAppsData())
}
}
Also the function should be called “mapDispatchToProps” but that is not important for your problem.
I believe calling
dispatch(fetchAppsData)
isn't correct, fetchAppsData is a thunk creator, not a thunk directly. Instead you would want to do
dispatch(fetchAppsData())
I cant figure out what is going on. I have redux-thunk setup just like always. For some reason that I can not figure out I get the error: Error: Actions must be plain objects. Use custom middleware for async actions. can anyone help me figure this error out?
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
);
CreateStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
export default function configureStore(initialState) {
return createStore(
reducers,
initialState,
applyMiddleware(thunk)
);
}
app.js
import React, { Component } from 'react';
import Routes from './Routes';
import {Provider} from 'react-redux';
import configureStore from './configureStore';
const store = configureStore();
class App extends Component {
render(){
return (
<Provider store={store}>
<Routes/>
</Provider>
);
}
}
export default App;
Today.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { autoLocate } from '../actions';
import { Button } from './Common'
class Today extends Component {
componentDidMount() {
this.props.fetchTodayWeather('http://
api.wunderground.com/api/cc9e7fcca25e2
ded/geolookup/q/autoip.json');
}
render() {
return (
<div>
<div style={styles.Layout}>
<div><Button> HAHAH </Button></div>
<div><Button> Weather Now </Button></div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
const loading = state.locate.loading;
const located = state.locate.autolocation;
return{ loading, located };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchTodayWeather:(Url) => dispatch(autoLocate(Url))
};
};
export default connect(mapStateToProps,mapDispatchToProps)(Today);`
autoLocate.js
import { AUTODATA,
AUTOLOCATING
} from './types';
export const autoLocate = (url) => {
return (dispatch) => {
dispatch({ type: AUTOLOCATING });
fetch(url)
.then(data => fetchSuccess(dispatch, data))
};
};
const fetchSuccess = (dispatch, data) => {
dispatch({
type: AUTODATA,
payload: data
});
};