My goal is to basically do a basic GET request in react-redux. I know how to do it with POST but not with GET because there is no event that is triggering the action.
Heres' the code for action
export function getCourses() {
return (dispatch) => {
return fetch('/courses', {
method: 'get',
headers: { 'Content-Type': 'application/json' },
}).then((response) => {
if (response.ok) {
return response.json().then((json) => {
dispatch({
type: 'GET_COURSES',
courses: json.courses
});
})
}
});
}
}
Where do i trigger this to get the data? in component?
import React from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import { getCourses } from '../actions/course';
class Course extends React.Component {
componentDidMount() {
this.props.onGetCourses();
}
allCourses() {
console.log(this.props.onGetCourses());
return this.props.courses.map((course) => {
return(
<li>{ course.name }</li>
);
});
return this.props
}
render() {
return (
<div>
<ul>
{ this.allCourses() }
</ul>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
courses: state.course.courses
}
}
const mapDispatchToProps = (dispatch) => {
return {
onGetCourses: () => dispatch(getCourses)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Course);
I tried this but it doesn't work.
Course Reducer
const initialState = {
courses: []
};
export default function course(state= initialState, action) {
switch (action.type) {
case 'GET_COURSES':
return Object.assign({}, state, {
courses: action.courses
})
default:
return state;
}
}
First, onGetCourses: () => dispatch(getCourses) should be changed to onGetCourses: () => dispatch(getCourses()) (you need to actually invoke the action creator).
When it comes to where you should call the action, it is absolutely fine to do it in componentDidMount, as you have done.
In case you did not notice, you have two return's in your allCourses().
I have similar code in my codebase, but I don't use return in front of fetch and response.json() because the function should return action object.
Related
I'm on my Home Component where I need to show the article feed and for that, I have to have the articleList array. But for some reason when I look into the store, articleList is null. Also, the console.log that I have placed after fetching the data is also not working. It all seems strange.
Home.js
import React, { Component } from "react"
import { connect } from "react-redux"
import { listAllArticles } from "../actions/articles"
class Home extends Component {
componentDidMount() {
this.props.dispatch(listAllArticles)
}
render() {
console.log(this.props)
return (
<div style={{ textAlign: "center" }}>
<h1>Conduit</h1>
<h5>A place to share your knowledge</h5>
</div>
)
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(Home)
listAllArticles
export const listAllArticles = () => {
console.log("inside listAllArticles action creator")
return dispatch => {
fetch("https://conduit.productionready.io/api/articles")
.then(res => res.json())
.then(data => {
console.log(data.articles)
dispatch({
type: "LIST_ALL_ARTICLES",
data: data.articles
})
})
}
}
articleReducer
const initState = {
articleList: null
}
export const articleReducer = (state=initState, action) => {
console.log("inside article reducer")
switch(action.type) {
case "LIST_ALL_ARTICLES":
return {...state, articleList: action.data}
default:
return state
}
}
Hello I have the following error when trying to consume my api
TypeError: api.get is not a function
api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:8000', });
export default api;
action fetch:
const api = require('../../services/api');
export function productsError(bool) {
return {
type: 'PRODUCTS_HAS_ERRORED',
hasErrored: bool
};
}
export function productsIsLoading(bool) {
return {
type: 'PRODUCTS_IS_LOADING',
isLoading: bool
};
}
export function productsFetchSuccess(products) {
return {
type: 'PRODUCTS_SUCCESS',
products
};
}
export function errorAfterFiveSeconds() {
// We return a function instead of an action object
return (dispatch) => {
setTimeout(() => {
// This function is able to dispatch other action creators
dispatch(productsError(true));
}, 5000);
};
}
export function ProductsFetchData() {
return (dispatch) => {
dispatch(productsIsLoading(true));
api.get('/products')
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(productsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((products) => dispatch(productsFetchSuccess(products)))
.catch(() => dispatch(productsError(true)));
};
}
reducer fetch
export function ProductsHasErrored(state = false, action) {
switch (action.type) {
case 'PRODUCTS_HAS_ERRORED':
return action.hasErrored;
default:
return state;
}
}
export function ProductsIsLoading(state = false, action) {
switch (action.type) {
case 'PRODUCTS_IS_LOADING':
return action.isLoading;
default:
return state;
}
}
return action.products;
default:
return state;
}
} return action.products;
default:
return state;
}
}export function Products(state = [], action) {
return action.products;
default:
return state;
}
} return action.products;
default:
return state;
}
}
my store :
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
in my app:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { bindActionCreators } from 'redux';
import { ProductsFetchData } from '../../store/actions/productsFetch';
class index extends Component {
componentDidMount() {
this.props.fetchData('/products');
}
render() {
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return (
<div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.products,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(ProductsFetchData())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(index);
basically I have error in this function:
export function ProductsFetchData() {
return (dispatch) => {
dispatch(productsIsLoading(true));
api.get('/products')
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(productsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((products) => dispatch(productsFetchSuccess(products)))
.catch(() => dispatch(productsError(true)));
};
}
I don't know why or where I went wrong to get this error
in action fetch, you should be change:
const api = require('../../services/api');
to:
const api = require('../../services/api').default;
or
import api from '../../services/api')
You should just export the baseURL as as const, then in your actions:
import axios from 'axios'
//othercode here
export function ProductsFetchData() {
return (dispatch) => {
dispatch(productsIsLoading(true));
api.get(`${}/products`)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(productsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((products) => dispatch(productsFetchSuccess(products)))
.catch(() => dispatch(productsError(true)));
};
}
When you use export default, This file will create an object with key is default and export them.
const a = 2;
export default a;
import with require:
const a = require(...)
console.log(a)
// a here will be an object
>> Object {default: 2}
So when you want to use require from export default, you have to access to .default: console.log(a.default).
Or you can use import in ES6 like this:
import a from '...';
// console.log(a)
>> 2
I am in need of guidance with getting through this error. The code is supposed to get the results from WebAPI while going through actions and services. In the actions is a dispatch where the error is. On my actions page it should call the service for WebAPI and depend on the response dispatch to the reducers for actions. The code does not pass the first dispatch in the jobActions.getjobs()
The error received from this is:
Unhandled Rejection (TypeError): _actions_job_actions__WEBPACK_IMPORTED_MODULE_1__.jobActions.getJobs(...).then is not a function
Page Load
import React from 'react';
import { jobActions } from '../../actions/job.actions';
class LoadTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
this.props.getJobs()
.then((res) => {
this.setState({ data: res.response || [] })
});
}
render() {
return ();
}
const mapDispatchToProps => dispatch => ({ getJobs: () => dispatch(jobActions.getJobs()) });
export default connect(mapDispatchToProps)( LoadTable );
===============================================
Actions
import { jobConstants } from '../constants/job.constants';
import { jobService } from '../services/job.service';
export const jobActions = {
getJobs
};
let user = JSON.parse(localStorage.getItem('user'));
function getJobs() {
return dispatch => {
dispatch(request());
return jobService.getJobs()
.then(
results => {
dispatch(success(user));
return { results };
},
error => {
dispatch(failure(error));
}
);
};
function request() { return { type: jobConstants.JOB_REQUEST }; }
function success(user) { return { type: jobConstants.JOB_SUCCESS, user }; }
function failure(error) { return { type: jobConstants.JOB_FAILURE, error }; }
}
=======================================================
services
export const jobService = {
getJobs
};
const handleResponseToJson = res => res.json();
function getJobs() {
return fetch('http://localhost:53986/api/jobs/getoutput')
.then(handleResponseToJson)
.then(response => {
if (response) {
return { response };
}
}).catch(function (error) {
return Promise.reject(error);
});
}
The result should be table data from the services page, actions page dispatching depending on the stage.
I assume you are using some sort of a middleware, like redux-thunk? If not, then your action creator returns a function, which is not supported by pure redux
I guess you do, because the error says that the action creator returned undefined after it was called
function getJobs() {
console.log("test -1");
return dispatch => {
console.log("test-2");
dispatch(request());
jobService.getJobs() // <==== here comes the promise, that you don't return
// return jobService.getJobs() <== this is the solution
.then(
results => {
console.log("test -3");
dispatch(success(user));
return { results };
},
error => {
dispatch(failure(error));
}
);
};
Update: you also need to map your action in mapDispatchToProps
Page Load
import React from 'react';
import { jobActions } from '../../actions/job.actions';
class LoadTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
this.props.getJobs() // as the name of mapDispatchToProps says, you mapped your action dispatch
// to a getJobs prop, so now you just need call it
.then((res) => {
this.setState({
data: res.response || []
})
}));
}
render() {
return ();
}
const mapStateToProps = state => ({});
const mapDispatchToProps = dispatch => ({
// this function will dispatch your action, but it also mapps it to a new prop - getJobs
getJobs: () => dispatch(jobActions.getJobs())
});
export default connect(mapStateToProps, mapDispatchToProps)( LoadTable );
I have a project in react and redux with immutablejs. It looks like this:
The Page looks like this:
function mapStateToProps(state) {
return {
content: state.content
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(thunks, dispatch)
};
}
class ConnectedConnect extends Component {
componentDidMount() {
const { actions } = this.props
actions.retrieveContent();
console.log(this.props.content)
}
render() {
return (
<div>
<Header heading="Demo Heading" colour="orange" disableAnimation={false} />
</div >
);
}
}
`const Content = connect(mapStateToProps, mapDispatchToProps)`(ConnectedConnect);
export default Content
The api:
export function viewContent() {
return fetch("http://localhost:8080/watchlist/content",
{
method: "GET",
}).then(function (response) {
if (!response.ok) {
throw Error(response.statusText);
}
// Read the response as json.)
return response.json();
})
.catch(function (error) {
console.log('Looks like there was a problem: \n', error);
})
}
The actions:
export function contentSuccess(retrieveContentWasSuccessful) {
return { type: types.RETRIEVE_CONTENT_SUCCESS, contentSuccess };
}
export function retrieveContent() {
// make async call to api, handle promise, dispatch action when promise is resolved
return function (dispatch) {
return viewContent().then(retrieveContentWasSuccessful => {
dispatch(contentSuccess(retrieveContentWasSuccessful));
}).catch(error => {
throw (error);
});
};
}
The reducer:
export function contentReducer(state = fromJS({}), action) {
switch (action.type) {
case types.RETRIEVE_CONTENT_SUCCESS:
return state.merge(action.content)
default:
return state;
}
};
The store itself is like this:
const history = createBrowserHistory()
const store = createStore(
connectRouter(history)(rootReducer),
applyMiddleware(
routerMiddleware(history),
thunkMiddleware
)
);
export { store, history };
The api is successfully called. However I can't seem to actually access the response in the store! Grateful for any help with this!
Thanks!
Think you want to pass retrieveContentWasSuccessful here instead of contentSuccess:
export function contentSuccess(retrieveContentWasSuccessful) {
return { type: types.RETRIEVE_CONTENT_SUCCESS, retrieveContentWasSuccessful };
}
I want to delete a post. I'm using redux-promise as middleware.
My action looks like this:
export function deletePost(id) {
const request = axios.delete(`${ROOT_URL}/${id}?apiKey=${API_KEY}`)
return {
type: DELETE_POST,
payload: request
}
}
Then I have a button in my component to trigger action.
onDeleteClick() {
deletePost(id)
.then(setState({ redirect: true }))
}
The problem is that I can't use 'then()'. I would simply like to redirect user to homepage after deleting post.
Please help me guys.
Source code on request.
actions/index.js
import axios from 'axios';
export const DELETE_POST = 'DELETE_POST';
export const ROOT_URL = 'example.com';
export const API_KEY = 'randomstring';
export function deletePost(id) {
const request = axios.delete(`${ROOT_URL}/${id}?apiKey=${API_KEY}`)
return {
type: DELETE_POST,
payload: request
}
}
reducers/post_reducer.js
import { DELETE_POST } from '../actions/index';
const INITIAL_STATE = { all: [], post: null };
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
return state.all.filter(post => post !== action.payload.data);
default:
return state;
}
}
components/PostShow.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPost, deletePost } from '../actions/index';
import { Link } from 'react-router-dom';
import { Redirect } from 'react-router';
class PostsShow extends Component {
constructor(props) {
super(props)
this.state = {
redirect: false
}
}
componentWillMount() {
this.props.fetchPost(this.props.match.params.id)
}
onDeleteClick() {
deletePost(this.props.match.params.id)
.then(() => this.setState({ redirect: true }))
}
render() {
if(!this.props.post) {
return <div>Loading...</div>
}
if(this.state.redirect) {
return <Redirect to='/'/>
}
return (
<div className='blog-post container'>
<h3>{this.props.post.title}</h3>
<h6>Categories: {this.props.post.categories}</h6>
<p>{this.props.post.content}</p>
<Link to='/'><button className='btn btn-primary'>Back</button></Link>
{ this.props.user
? <button className='btn btn-danger' onClick={this.onDeleteClick.bind(this)}>Delete</button>
: null }
</div>
);
}
}
function mapStateToProps(state) {
return {
post: state.posts.post,
user: state.user
}
}
export default connect(mapStateToProps, { fetchPost, deletePost }) (PostsShow)
Per the redux-promise source code, it looks like it should return the chained promise:
return isPromise(action.payload)
? action.payload.then(
result => dispatch({ ...action, payload: result }),
error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
}
)
: next(action);
So, I would assume that you could chain off of the dispatch.
That said, your snippet has a couple potential issues. If that's in a component, then where is deletePost coming from? You're also not using this in front of setState. Assuming that deletePost is a bound-up action creator, this example should be correct:
onDeleteClick() {
this.props.deletePost(id)
.then(() => this.setState({redirect : true});
}