I have such action:
import { GET, POST, PUT, REMOVE } from "../../Utils/Http";
export const FETCH_ARTICLES = "FETCH_ARTICLES";
export const FETCH_ARTICLES_SUCCESS = "FETCH_ARTICLES_SUCCESS";
export const FETCH_ARTICLES_FAILURE = "FETCH_ARTICLES_FAILURE";
export const RESET_ARTICLES = "RESET_ARTICLES";
export function fetchArticles() {
const request = GET("/articles");
return {
type: FETCH_ARTICLES,
payload: request
};
}
export function fetchArticlesSuccess(articles) {
return {
type: FETCH_ARTICLES_SUCCESS,
payload: articles
};
}
export function fetchArticlesFailure(error) {
return {
type: FETCH_ARTICLES_FAILURE,
payload: error
};
}
and reducer:
import {
FETCH_ARTICLES,
FETCH_ARTICLES_SUCCESS,
FETCH_ARTICLES_FAILURE,
RESET_ARTICLES
} from "../Actions/Article";
const INITIAL_STATE = {
articlesList: {
articles: { data: [], total: 0 },
error: null,
loading: false
},
newTractor: { article: null, error: null, loading: false },
activeTractor: { article: null, error: null, loading: false },
deletedTractor: { article: null, error: null, loading: false }
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: true }
};
case FETCH_ARTICLES_SUCCESS:
return {
...state,
articleList: { articles: action.payload, error: null, loading: false }
};
case FETCH_ARTICLES_FAILURE:
return {
...state,
articleList: { articles: {}, error: action.payload, loading: false }
};
case RESET_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: false }
};
default:
return state;
}
};
export default reducer;
And i try it to use this way in list component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { isUndefined } from "lodash";
import {
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure
} from "../../Store/Actions/Article";
class ArticleList extends Component {
componentDidMount() {
this.props.fetchArticles();
}
render() {
return <div className="ui segment" />;
}
}
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
export default connect(null, mapDispatchToProps)(ArticleList);
also Http.js:
import axios from "axios";
const http = axios.create({
baseURL: process.env.BASE_API_URL
});
export const GET = (url, params) => {
return new Promise((resolve, reject) => {
http({
method: "get",
url,
params
})
.then(response => {
resolve(response);
})
.catch(err => {
console.log("GET err ", err);
reject(err);
});
});
};
...
But as result I get:
TypeError: dispatch is not a function in dispatch(fetchArticles()).then(response => {
What I do wrong?
Also how can i write this part:
fetchTractors()).then(response => {
!response.error
? dispatch(fetchTractorsSuccess(response.payload.data))
: dispatch(fetchTractorsFailure(response.payload.data));
}
in component class? is it possible? (not to move it to the mapDispatchToProps block)
i took some ideas from here: https://github.com/rajaraodv/react-redux-blog/
I can see many problems here:
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
dispatch is a synchronous thing by default unless you have configured some middleware such as redux-thunk to handle functions. dispatch takes native object as an argument in normal scenario.
dispatch does not return a promise. So then can not be used,
connect takes first arguments as mapStateToProps and second argument as mapDispatchtoProps. There is also third argument which is not generally used. So I will not mention it for now.
4.you need to pass the actions creators through mapDispatchToProps like this:
import { bindActionCreators } from "redux"
const mapDispatchToProps = dispatch => bindActionCreators({
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure,
}, dispatch)
The probles is here:
export default connect(mapDispatchToProps)(ArticleList);
First parameter should be mapStateToProps. But you actually can pass null:
export default connect(null, mapDispatchToProps)(ArticleList);
If someone encountered this problem while using ts + redux, the IDE prompted you that there is no then method, you can refer to this link
Related
I'm facing weird behavior while fetching the records in redux saga. When I try to call an action in useeffect from the class, it is being called in loop multiple times. For that reason, there are infinite api calls.
Can anybody tell me where i'm wrong please? Following is my code.
Reducer
// #flow
import {
FETCH_DOCTOR_PROFILE,
FETCH_DOCTOR_PROFILE_SUCCESS,
FETCH_DOCTOR_PROFILE_ERROR
} from "./actionTypes";
const INIT_STATE = {
error: null,
isLoading: false,
doctorProfile: {}
};
const DoctorProfileReducer = (state = INIT_STATE, action) => {
switch (action.type) {
case FETCH_DOCTOR_PROFILE:
return {
...state,
isLoading: true
};
case FETCH_DOCTOR_PROFILE_SUCCESS:
return {
...state,
isLoading: false,
doctorProfile: action.payload
};
case FETCH_DOCTOR_PROFILE_ERROR:
return {
...state,
isLoading: false,
error: action.error
};
default:
return state;
}
};
export default DoctorProfileReducer;
Action
import {
FETCH_DOCTOR_PROFILE,
FETCH_DOCTOR_PROFILE_SUCCESS,
FETCH_DOCTOR_PROFILE_ERROR
} from "./actionTypes";
export const fetchDoctorProfileAction= () => ({
type: FETCH_DOCTOR_PROFILE
});
export const fetchDoctorProfileSuccessAction= (doctorProfile) => ({
type: FETCH_DOCTOR_PROFILE_SUCCESS,
payload: doctorProfile
});
export const fetchDoctorProfileErrorAction= (error) => ({
type: FETCH_DOCTOR_PROFILE_ERROR,
error: error
});
Saga
import { takeEvery, fork, put, all, call } from 'redux-saga/effects';
// Redux States
import { FETCH_DOCTOR_PROFILE } from './actionTypes';
import { fetchDoctorProfileSuccessAction, fetchDoctorProfileErrorAction } from './actions';
import { fetchDoctorProfileApi } from '../../../services/doctorProfile';
import {FETCH_DOCTOR_PROFILE_URL} from '../../../helpers/urls';
function* fetchDoctorProfileSaga() {
try {
const response = yield call(fetchDoctorProfileApi,FETCH_DOCTOR_PROFILE_URL);
yield put(fetchDoctorProfileSuccessAction(response));
} catch (error) {
yield put(fetchDoctorProfileErrorAction(error));
}
}
export function* watchFetchDoctorProfile() {
yield takeEvery(FETCH_DOCTOR_PROFILE, fetchDoctorProfileSaga)
}
function* doctorProfileSaga() {
yield all([
fork(watchFetchDoctorProfile),
]);
}
export default doctorProfileSaga;
Calling page
useEffect(() => {
props.fetchDoctorProfileAction();
const result = props.doctorProfile;
});
...........
const mapStateToProps = (state) => {
const { error, doctorProfile, pending } = state.DoctorProfileReducer;
return { error , doctorProfile, pending };
}
export default withRouter(connect(mapStateToProps, {fetchDoctorProfileAction})(ProfessionalProfilePrimary));
I think you need to add a condition in your useEffect() hook:
useEffect(() => {
if (!props.doctorProfile && !props.pending) {
props.fetchDoctorProfileAction();
}
const result = props.doctorProfile;
}, [props.doctorProfile, props.pending]);
NOTE: in your component mapStateToProps you have pending but in your store you have isLoading; make sure you are correctly mapping the state to the prop ;)
I converted this application
https://codesandbox.io/s/3y77o7vnkp <--Please check this link
into my react-native app and it works perfect. Since I implemented redux and redux-thunk I have a problem with fetching data.
There is a problem. I converted normal functions from this upper link to actions and reducers in react-native. For Example
handleSelect = itemValue => {
this.setState(
{
...this.state,
base: itemValue,
result: null,
},
this.calculate
);
};
TO
actiontypes.js
export const HANDLE_FIRST_SELECT = 'HANDLE_FIRST_SELECT';
action.js
export const handleFirstSelect = itemValue => {
return {
type: actionTypes.HANDLE_FIRST_SELECT,
itemValue: itemValue,
};
};
and reducer.js
const initialState = {
currencies: ['USD', 'AUD', 'SGD', 'PHP', 'EUR', 'PLN', 'GBP'],
base: 'EUR',
amount: '',
convertTo: 'PLN',
result: '',
date: '',
error: null,
loading: false,
};
const exchangeCurrencies = (state = initialState, action) => {
switch (action.type) {
case actionTypes.HANDLE_FIRST_SELECT:
return {
...state,
base: action.itemValue,
result: null,
};
...
Next step I used mapStateToProps and mapDispatchToProps in my component like this
const mapDispatchToProps = dispatch => {
return {
handleFirstSelect: itemValue =>
dispatch(exchangeCurriencesActions.handleFirstSelect(itemValue)),
...
const mapStateToProps = (state) => {
return {
base: state.base,
amount: state.amount,
convertTo: state.convertTo,
result: state.result,
date: state.date,
};
};
And I'm using now this.props
<PickerComponent
selectedValue={this.props.base}
onValueChange={this.props.handleFirstSelect}
/>
Until then, everything works ok. Now when I download data in this way with react-redux and redux-thunk (action.js) it stops working
export const fetchDataSuccess = data => {
return {
type: actionTypes.FETCH_DATA_SUCCESS,
data: data,
};
};
export const fetchDataFail = error => {
return {
type: actionTypes.FETCH_DATA_FAIL,
error: error,
};
};
export const fetchData = () => {
return dispatch => {
fetch(`https://api.exchangeratesapi.io/latest?base=${this.props.base}`)
.then(res => res.json())
.then(
data => dispatch(fetchDataSuccess(data.rates)),
e => dispatch(fetchDataFail(e)),
);
};
};
next reducer.js
...
case actionTypes.FETCH_DATA_BEGIN:
return {
...state,
loading: true,
error: null,
};
case actionTypes.FETCH_DATA_SUCCESS:
console.log('data', action.data);
return {
...state,
date: action.data.date,
result: action.data.rates,
loading: false,
};
case actionTypes.FETCH_DATA_FAIL:
console.log('data', action.error);
return {
...state,
loading: false,
error: action.error,
};
...
In the next step added function fetchData into mapDispatchToProps and call this in componentDidMount like that
componentDidMount() {
if (this.props.amount === isNaN) {
return;
} else {
try {
this.props.fetchData();
} catch (e) {
console.log('error', e);
}
}
}
and finnaly I add calculations for currencies in mapStateToProps. I change result like that
result: (state.result[state.convertTo] * state.amount).toFixed(4),
and also I added applymiddleware into the store.
AND FINNALY THERE IS A ERROR
import React from 'react';
import HomeContentContainer from '../../containers/HomeContentContainer/HomeContentContainer';
class HomeScreen extends React.Component {
render() {
return <HomeContentContainer />;
}
}
export default HomeScreen;
Anyone know how to resolve this problem? Where and what should I change the code?
Following up on the comments to the question, please do review those...
In mapStateToProps protect your access to state.result, like this perhaps:
result: state.result ? (state.result[state.convertTo] * state.amount).toFixed(4) : null
, where null could also be 0 or whatever, your call. Your mapStateToProps is indeed getting called (at least) once before the API request is completed, so it needs to handle your initial state of state.result.
I find it confusing that the components property result and the state.result are different things, but this is just my opinion about naming.
My current React Native Expo app has a ScrollView that implements RefreshControl. A user pulling down the ScrollView will cause the onRefresh function to be executed, which in turns call an action creator getSpotPrices that queries an API using axios.
Problem: If there is a network problem, the axios.get() function will take very long to time out. Thus, there is a need to implement the timing out of either axios.get() or onRefresh.
How can we implement a timeout function into RefreshControl?
/src/containers/main.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ScrollView, RefreshControl } from 'react-native';
import MyList from '../components/MyList';
import { getSpotPrices } from '../actions';
class RefreshableList extends Component {
onRefresh = () => {
this.props.getSpotPrices();
}
render() {
return (
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.props.isLoading}
onRefresh={this._onRefresh}
/>
}>
<MyList />
</ScrollView>
)
}
}
const mapStateToProps = (state) => {
return {
isLoading: state.currencies.isLoading,
}
}
const mapDispatchToProps = (dispatch) => {
return {
getSpotPrices: () => dispatch(getSpotPrices()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RefreshableList);
/src/actions/index.js
import api from "../utils/api";
import * as types from "../types";
import Axios from "axios";
const getSpotPrice = async () => {
try {
const res = await Axios.get(`https://api.coinbase.com/v2/prices/spot`);
return parseFloat(res.data.data.amount);
} catch (err) {
throw new Error(err);
}
};
export const getSpotPrices = () => async dispatch => {
try {
const price = await getSpotPrice();
dispatch({
type: types.CURRENCIES_SET,
payload: price
});
} catch (err) {
dispatch({
type: types.CURRENCIES_FAILED_FETCH,
payload: err.toString()
});
} finally {
dispatch({
type: types.CURRENCIES_IS_LOADING,
payload: false
})
}
};
/src/reducers/currencies.js
import * as types from "../types";
const initialState = {
data: {},
isLoading: false,
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case types.CURRENCIES_SET:
return {
...state,
data: payload,
error: "",
isLoading: false
};
case types.CURRENCIES_FAILED_FETCH:
return {
...state,
error: payload,
isLoading: false
};
case types.CURRENCIES_IS_LOADING:
return {
isLoading: payload
}
default:
return state;
}
};
Check if user is connected internet or not using the react-native-netinfo library
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
this.setState({ connected: state.isConnected });
});
// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
console.log("Connection type", state.type);
this.setState({ connected: state.isConnected });
});
// Unsubscribe
unsubscribe(); <- do this in componentwillunmount
Its generally a good practice to add a timeout, in all your api calls, in axios you can easily add a timeout option like:
await axios.get(url, { headers, timeout: 5000 })
so in your case modify the axios call as
await Axios.get(https://api.coinbase.com/v2/prices/spot, { timeout: 5000 } );
I have put timeout of 5 seconds you can modify the parameter according to your need.
I am new to redux and I am having a hard time understanding how to connect the payload of my API call to my state.
Right now my action.js file looks like this:
import ApiService from '../../services/ApiService';
import { reset } from 'redux-form';
//actions
export const getStock = () => {
return {
type: 'GET_STOCK'
}
}
export const getStockPending = () => {
return {
type: 'GET_STOCK_PENDING'
}
}
export const getStockFulfilled = (stock) => {
return {
type: 'GET_STOCK_FULFILLED',
payload: stock
}
}
export const getStockRejected = () => {
return {
type: 'GET_STOCK_REJECTED'
}
}
// async function calls
export function fetchStocksWithRedux() {
const action_type = "GET_STOCK";
const stock = 'AAPL';
return (dispatch) => {
dispatch({type: `${action_type}_PENDING`});
return ApiService.get(`/search?query=${stock}`)
.then(([response, json]) =>{
if(response.status === 200){
dispatch(getStockFulfilled(json))
}
else{
dispatch(getStockRejected())
}
})
}
}
and my reducer.js file looks like this:
const initialState = {
inProgress: false,
stock: {},
stocks: ['NKE', 'AMZN', 'AAPL'],
error: {}
}
export default (state = initialState, action) => {
switch(action.type) {
case 'GET_STOCK_PENDING':
return {
...state,
inProgress: true,
error: false
}
case 'GET_STOCK_FULFILLED':
return {
...state,
stock: action.payload,
inProgress: false
}
case 'GET_STOCK_REJECTED':
return {
...state,
inProgress: false,
error: action.error
}
default:
return state;
}
}
When I go to call my method fetchStocksWithRedux in my component, the network tab in my dev tools shows a 200 status and the response I'm expecting, but the reducer dispatches the 'GET_STOCK_REJECTED' action, but the error hash is empty. What do you think is going wrong?
Here is my component, for reference:
import React, { Component } from 'react';
import { fetchStocksWithRedux } from '../../redux/modules/Stock/actions';
import { connect } from 'react-redux';
class Dashboard extends Component {
componentDidMount() {
this.props.fetchStocksWithRedux()
}
render() {
return (
<div className="uk-position-center">
</div>
)
}
}
export default connect(
state => ({
stocks: state.stocks,
stock: state.stock
})
, { fetchStocksWithRedux }
)(Dashboard);
Thanks. Any advice or guidance would be greatly appreciated!
So I'm learning redux currently and I'm making an app that displays a list of articles. But I can't figured out why my data from my back end isn't showing up. I'm not sure where my error is that preventing my data from my backend from showing up? I know it not the setup of redux because I did simpler app to see if that was the problem and it wasn't so it has to do more with the action, reducers , and component. I would like to go farther eventually when there is more data in the database where it provides a link so it goes to another page that shows all the information about that article.
data from my node backend
[{"_id":"58c71df9f7e4e47f1fe17eeb","article":"words words","author":"Jason","date":"1/2/2014","title":"my article","__v":0}]
fashionActions.js
import axios from "axios";
export function fetchFashionArticle() {
return function(dispatch) {
axios.get("http://localhost:3000/api/fashion")
.then((response) => {
dispatch({type: "FETCH_FASHIONARTICLES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_FASHIONARTICLES_REJECTED", payload: err})
})
}
}
export function addFashionArticle(_id, title,date, author, article) {
return {
type: "ADD_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function updateFashionArticle(_id, title,date, author, article) {
return {
type: "UPDATE_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function deleteFashionArticle(id) {
return {type: 'DELETE_FASHIONARTICLE', payload: id}
}
FashionArticle.js
import React from "react";
import { connect } from "react-redux";
import {fetchFashionArticle} from "../actions/fashionActions";
#connect((store) => {
return {
fashionarticles:store.fashionarticles.fashionarticles,
};
})
export default class FashionArticle extends React.component {
fetchFashionArticle() {
this.props.dispatch(fetchFashionArticle())
}
render() {
const { fashionarticles } = this.props;
if(!fashionarticles.length) {
return <button onClick={this.fetchFashionArticles.bind(this)}>Load articles</button>
}
const mappedArticles = fashionarticles.map(fashionarticle => <li>{fashionarticle}</li>)
return(
<div>
<h1>Fashion Article</h1>
<h2>{fashionarticles.title}</h2>
</div>
)
}
}
fashionArticleReducers.js
export default function reducer(state={
fashionarticles: [],
fetching: false,
fetched: false,
error: null,
}, action) {
switch (action.type) {
case "FETCH_FASHIONARTICLES": {
return {...state, fetching: true}
}
case "FETCH_FASHIONARTICLES_REJECTED": {
return {...state, fetching: false, error: action.payload}
}
case "FETCH_FASHIONARTICLES_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
fashionarticles: action.payload,
}
}
case "ADD_FASHIONARTICLE": {
return {
...state,
fashionarticles: [...state.fashionarticles, action.payload],
}
}
case "UPDATE_FASHIONARTICLE": {
const { _id, title,date,author,article } = action.payload
const newFashionArticles = [...state.fashionarticles]
const fashionarticleToUpdate = newFashionArticles.findIndex(fashionarticle => fashionarticle.id === id)
newFashionArticles[fashionarticleToUpdate] = action.payload;
return {
...state,
fashionarticles: newFashionArticles,
}
}
case "DELETE_FASHIONARTICLE": {
return {
...state,
fashionarticles: state.fashionarticles.filter(fashionarticle => fashionarticle.id !== action.payload),
}
}
}
return state
}
index.js
import { combineReducers } from 'redux';
import user from './testReducers'
import fashionarticles from './fashionArticleReducers';
export default combineReducers({
user,
fashionarticles,
})
You're sending the payload with the axios response as type FETCH_FASHIONARTICLES_DONE but your reducer is listening for FETCH_FASHIONARTICLES_FULFILLED