I have following Action:
import axios from 'axios';
export function getAPIData(id)
{
return (dispatch) =>
{
axios.get('http://localhost:5000/api/' + id)
.then(res =>
{
dispatch(
{
type: 'DONE',
payload: res.data
});
});
}
}
Then in my Component I`m dispatching the action:
componentDidMount()
{
this.props.dispatch(getAPIData());
}
And then:
function mapStateToProps(state)
{
console.log(state);
return {
data: state.result
};
}
export default connect(mapStateToProps)(Rows);
In console, when I try to find the payload, it says what is bellow.
function()
arguments: TypeError: 'arguments', 'callee', and 'caller' cannot be
accessed in this context.
caller: TypeError: 'arguments', 'callee', and 'caller' cannot be
accessed in this context.
length: 1
name: ""
Where is problem? Thanks a lot.
to dispatch an action you need to provide mapDispatchToProps .
First import your action
import { getAPIData } from "../store/actions/getAPIData";
then build mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
return {
getAPIData: (props = null) => {
dispatch(getAPIData(props));
},
};
};
add this alongside mapStateToProps
export default connect(mapStateToProps , mapDispatchToProps)(Rows);
now you can call the action like this
componentDidMount()
{
this.props.getAPIData();
}
Related
I have an api that give my react app data
{
"data_for_action1": ...
"data_for_action2": ...
"data_for_action3": ...
}
To propagate the data through redux I can
this.props.reduxAction1({
"data_for_action1": ....
})
this.props.reduxAction2({
"data_for_action2": ....
})
this.props.reduxAction3({
"data_for_action3": ....
})
export default connect(
mapStateToProps,
{ reduxAction1, reduxAction2, reduxAction3 }
)(MyComponent);
Call all redux action successively, but the problem is that I have to do this for all component that use this api.
Is there a way to split this in action function instead
export const myCombineReduxAction = myInfo => {
// call reduxAction1, reduxAction2, and reduxAction3
}
instead of
export const reduxAction1 = myInfo => ({
type: ActionType1,
payload: { myInfo }
});
export const reduxAction2 = myInfo => ({
type: ActionType2,
payload: { myInfo }
});
export const reduxAction3 = myInfo => ({
type: ActionType3,
payload: { myInfo }
});
I think so:-
You could create a custom function in your reducer:
given if all your action use the same dispatch from the same reducer
args or props passed to the custom function could be anything. But a make sure to pass the dispatch too
in reducer:-
export const myCombineReduxAction = (dispatch, myInfo) => {
// then run whatever dispatch here
dispatch({
type: ActionType1,
payload: { myInfo }
})
dispatch({
type: ActionType2,
payload: { myInfo }
})
dispatch({
type: ActionType2,
payload: { myInfo }
})
}
in demo component:-
import { useDispatch } from "react-redux";
import { myCombineReduxAction } from "../redux/reducer/whateverReducer";
const Demo = () => {
const dispatch = useDispatch();
const getData = () => {
const myInfo = 'something'
myCombineReduxAction(dispatch, myInfo)
}
return (<>Something</>)
}
If you use thunk you can do the following:
export const myCombineReduxAction = (myInfo) => (
dispatch
) => {
dispatch(reduxAction1(myInfo));
dispatch(reduxAction2(myInfo));
dispatch(reduxAction3(myInfo));
};
I have a React app that uses redux-thunk and axios to fetch an API. The action fires successfully, but returns multiple payloads which means it is firing multiple times.
How can I make it fire only one time?
Code
Actions
import Axios from "axios";
import { fetchEnglishLeagueTable } from "./ActionTypes";
export function fetchEnglishTable() {
var url = "https://api.football-data.org/v2/competitions/PL/matches";
var token = "52c8d88969d84ac9b17edb956eea33af";
var obj = {
headers: { "X-Auth-Token": token }
};
return dispatch => {
return Axios.get(url, obj)
.then(res => {
dispatch({
type: fetchEnglishLeagueTable,
payload: res.data
});
})
.catch(e => {
console.log("Cant fetch ", e);
});
};
}
Reducers
import { fetchEnglishLeagueTable } from "../actions/ActionTypes";
const initialState = {
EnglishTable: {}
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case fetchEnglishLeagueTable:
return {
...state,
EnglishTable: action.payload
};
default:
return state;
}
};
export default rootReducer;
Page
const League = props => {
useEffect(() => {
props.getLeagueTable();
}, [props.leagueTable]);
console.log(props.leagueTable);
return <p>ihi</p>;
};
const mapStateToProps = state => ({
leagueTable: state.EnglishTable
});
const mapDispatchToProps = dispatch => {
return { getLeagueTable: () => dispatch(fetchEnglishTable()) };
};
export default connect(mapStateToProps, mapDispatchToProps)(League);
Store
import rootReducer from "./Reducer";
import thunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
Here is what it returns
Just remove leagueTable from useEffect's dependency array, so it'll fetch them only once component is mounted. Because now you have a loop:
Get leagues -> leagueTable updates -> useEffect sees that leagueTable changed in dependency array and calls to get leagues again and we've got a loop.
const League = props => {
useEffect(() => {
props.getLeagueTable();
}, []); // <~ no props.leagueTable here
console.log(props.leagueTable);
return <p>ihi</p>;
};
Hope it helps :)
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'm using redux with redux-observable and get this strange error:
Actions must be plain objects. Use custom middleware for async >actions.
/* Component.jsx */
import React from "react"
import { serialNumberCheck } from '../actions'
const Component = props => {
...
<button
onClick={() => props.serialNumberCheck('123456789123456')}
>
Check
</button>
...
}
const mapDispatchToProps = dispatch =>
bindActionCreators({serialNumberCheck}, dispatch)
export default compose(
reduxForm({
...
}),
withStyles(styles),
connect(mapDispatchToProps)
)(Component)
/* actions.js */
export const SERIAL_NUMBER_CHECK = 'SERIAL_NUMBER_CHECK'
export const SERIAL_NUMBER_CHECK_SUCCESS = 'SERIAL_NUMBER_CHECK_SUCCESS'
export const serialNumberCheck = (serialNumber) => ({
type: SERIAL_NUMBER_CHECK,
payload: serialNumber
})
export const serialNumberCheckSuccess = (data) => ({
type: SERIAL_NUMBER_CHECK,
payload: data
})
/* epics.js */
...
import { serialNumberCheck } from "../actions"
import ... from 'rxjs'
...
function serialNumberCheckEpic(action$) {
return action$
.ofType(SERIAL_NUMBER_CHECK)
.switchMap((data) => {
return ajax.getJSON(`http://localhost:3004/sn/?sn=${data.payload}`)
.map((data) => data)
})
.map(data => {
if(data.length !== 0) {
serialNumberCheckSuccess({success: true});
}
})
}
...
export const rootEpic = combineEpics(
...
serialNumberCheckEpic
);
/* reducer.js */
import {
SERIAL_NUMBER_CHECK_SUCCESS,
} from '../actions'
...
export default function epicReducer(state = initialState, action) {
switch (action.type) {
case SERIAL_NUMBER_CHECK_SUCCESS:
return {
...state,
success: action.payload
}
}
}
/* JSON-SERVER RESPONSE */
[
{
"id": 1,
"sn": "123456789123456"
}
]
Inside component i'am calling function serialNumberCheck() and passing inside sierial number that we need to check.
Inside Epic im passing serial number to json-server that checks if this number exists in my "database". If serial number exists, server response is .json containing some parameters.
So if response isn't empty we need to write success: true inside redux store.
But in the end we get successfull GET request, and then error: Actions must be plain objects. Use custom middleware for async actions., but no changes inside redux-store and nothing from SERIAL_NUMBER_CHECK_SUCCESS action.
Finally, I found the solution. I've just missed the return before calling action inside my epic.
function serialNumberCheckEpic(action$) {
return action$
.ofType(SERIAL_NUMBER_CHECK)
.switchMap((data) => {
return ajax.getJSON(`http://localhost:3004/sn/?sn=${data.payload}`)
.map((data) => data)
})
.map(data => {
if(data.length !== 0) {
+ return serialNumberCheckSuccess({success: true});
}
})
}
So I do this in container:
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators({fetchMarkers}, dispatch)
}
}
and when props is logged in browser I get:
actions: {
fetchMakers: function() ....
}
but when I call this.props.actions.fetchMarkers(params) I get the following error:
Cannot read property 'fetchMarkers' of undefined
This is driving me nuts, Please help!
Edit:
Action:
export const fetchMarkers = (payload) => {
return {
type: actionTypes.fetchMarkers,
payload
}
}
Try this case:
import { fetchMarkers } from 'path_to_makers_action';
....Code of react component....
export default connent(null, { fetchMarkers });
And in component you use: this.props.fetchMarkers()