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 );
Related
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();
}
I am consoling state right after my function call in componentDidMount but it's giving data as EMPTY String.
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}
getData = () => {
functionApiCall().then(res => {
this.setState({
data: res.data
}); // Here the state is getting set
})
}
componentDidMount() {
this.getData();
console.log(this.state.data); //Empty string
}
render() {
return <></>;
}
}
export default App;
Any help will be appreciated.Thank you
Well, I think the api call is returning null , maybe change it like this
getData = () => {
functionApiCall().then(res => {
if(res && res.data) {
this.setState({
data: res.data
})// Here the state is getting set
}
}
}
Above should be fine, but just in case try this
getData = () => {
return new Promise(function(resolve, reject) {
functionApiCall().then(res => {
if(res && res.data) {
this.setState({
data: res.data
}, () => { resolve(res.data) })// Here the state is getting set
}
} });
}
And componentDidMount wait for your promise which resolves after state is set
async componentDidMount(){
await this.getData();
console.log(this.state.data) //NULL
}
setState is asynchronous so you cannot immediately access it.
You can render conditionally like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
getData = () => {
functionApiCall().then(res => {
this.setState({
data: res.data
});
});
};
componentDidMount() {
this.getData();
}
render() {
if (!this.state.data) {
return <div>Loading...</div>;
} else {
return <div>Data: {JSON.stringify(this.state.data)}</div>;
}
}
}
export default App;
Sample codesandbox with a fake api
I'm trying to implement socket.io library to a React application for practice. It's my first time I implement redux in any kind for application.
Describe the problem
I created a component Chatroom.js where in componentDidMount I dispatch an action to connect to the socket and listen for events.
componentDidMount() {
console.log('---Chatroom did mount')
console.log('isLoaded: ' + this.props.socket.isLoaded)
// if I remove this if-statement the compoenent re-renders
// and a new socket is created
if (!this.props.socket.isLoaded) {
this.props.userLoginToSocket()
.then(() => this.props.receive())
.then(() => this.props.setLoaded(true))
.then(() => this.props.sendUsername(this.props.auth.user.username))
}
}
I implemented a redux middleware to handle the socket.io communication as proposed in this post.
When I start the application I get this log
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: false
Chatroom.js:76 ---Chatroom will unmount
Messages.js:38 Messages component didMount
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: true
And the componentWillReceiveProps never gets executed.
I don't think that this is the expected behaviour and the componentDidMount should be only called once. Furthermore I cannot understand why the componentWillUnmount gets fired.
When I recieve a message from the server the log is
Chatroom.js:76 ---Chatroom will unmount
Messages.js:38 Messages component didMount
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: true
which clearly indicates that every time I dispatch an action the component unmounts and remounts.
Full Code
You can find the full project's code at github.
// ./Chatroom.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { userLoginToSocket, receive, sendUsername, disconnect, setLoaded, emit } from '../actions/socketAction';
import Messages from './Messages'
class Chatroom extends Component {
constructor(props) {
super(props)
this.handleSend = this.handleSend.bind(this)
}
componentDidMount() {
console.log('---Chatroom did mount')
// console.log('socket.isConnected: ' + this.props.socket.isConnected)
// console.log('socket.isConnecting: ' + this.props.socket.isConnecting)
console.log('isLoaded: ' + this.props.socket.isLoaded)
// if I remove this if-statement the compoenent re-renders
// and a new socket is created
if (!this.props.socket.isLoaded) {
this.props.userLoginToSocket()
.then(() => this.props.receive())
.then(() => this.props.setLoaded(true))
.then(() => this.props.sendUsername(this.props.auth.user.username))
}
}
componentWillUnmount() {
// every time a new message is recieved the component willUnmount
// i want on component will unmount to disconnect from the socket
console.log('---Chatroom will unmount')
}
componentWillReceiveProps(nextProps) {
console.log('---Component will receive props')
}
handleSend() {
this.props.emit('Hello')
}
render() {
return (
<div>
<h1>Chatroom</h1>
{ this.props.socket.isLoaded &&
<Messages messages={this.props.messages.messages}/>
}
<button onClick={this.handleSend}>Send</button>
</div>
);
}
}
Chatroom.propTypes = {
socket: PropTypes.object,
messages: PropTypes.object
}
const mapStateToProps = (state) => {
return({
socket: state.socket,
messages: state.messages
})
}
export default withRouter(
connect(mapStateToProps, { userLoginToSocket , receive, sendUsername, disconnect, setLoaded, emit })(Chatroom)
)
// ./Messages.js
import React, { Component } from 'react'
class Messages extends Component {
componentDidMount() {
console.log('Messages component didMount')
}
render() {
return (
<div>
<h3>Messages</h3>
<ul>
{this.props.messages.map((item, ind) => <li key={ind}>{item.message}</li>)}
</ul>
</div>
)
}
}
export default Messages
Actions
// ../actions/socketAction
export const setLoaded = (boolean) => {
return {
type : "SET_LOADED",
boolean
}
}
export const userLoginToSocket = () => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["CONNECT", "CONNECT_SUCCESS", "CONNECT_FAIL"],
promise: (socket) => socket.connect()
});
}
}
export function disconnect() {
return {
type: 'socket',
types: ["DISCONNECT", "DISCONNECT_SUCCESS", "DISCONNECT_FAIL"],
promise: socket => socket.disconnect(),
}
}
export const receive = () => {
return (dispatch) => {
const newMessage = (message) => {
return dispatch({
type: "NEW_MESSAGE_FROM_SOCKET",
payload: message,
});
};
return dispatch({
type: 'socket',
types: ["RECEIVE_", "RECEIVE_SUCC", "RECEIVE_FAIL"],
promise: (socket) => socket.on('ReceiveMessage', newMessage),
});
}
}
export const sendUsername = (user) => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["SEND_USER", "SEND_USER_SUCCESS", "SEND_USER_FAIL"],
promise: (socket) => socket.emit('SET_USERNAME', user),
});
}
}
export const emit = (message) => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["SEND", "SEND_SUCCESS", "SEND_FAIL"],
promise: (socket) => socket.emit('SEND_MESSAGE', message),
});
}
}
Reducers
// ../socketReducer.js
const initialState = {
isConnected: false,
isConnecting: false,
isLoaded: false,
messageRecieved: false
}
export default function socketReducer(state=initialState, action) {
switch (action.type) {
case "CONNECTED":
return {
...state,
isConnected: true
}
case "DISCONNECTED":
return {
...state,
isConnected: false
}
case "NEW_MESSAGE_FROM_SOCKET":
return {
...state,
messageRecieved: true
}
case "SET_LOADED":
return {
...state,
isLoaded: action.boolean
}
default:
return state
}
}
// ../messagesReducer.js
const initialState = {
messages: [{message: "initial"}],
isRecieving: false,
didRecieve: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "NEW_MESSAGE_FROM_SOCKET":
return {
...state,
messages: [...state.messages, action.payload]
}
default :
return state
}
}
I have an action creator that I'm calling in componentWillMount, the return of that action payload is being assigned to state using setState. However, in componentDidMount I cannot access that property as the async call hasn't completed yet. What is the correct way to access this data in compoentDidMount?
//component
class Dashboard extends Component {
componentWillMount() {
this.setState(this.props.getUser());
}
componentDidMount() {
// this.state.user isn't available yet
}
render(){
return(...);
}
}
//action
export function getUser() {
return async function (dispatch) {
const user = await axios.get(`${API_URL}user?token=${token}`);
return dispatch({
type: USER,
payload: user,
});
}
};
}
Axios returns a promise and you have to wait until it resolves. Then dispatch the success action like this,
export function getUser() {
return function (dispatch) {
axios.get(`${API_URL}user?token=${token}`)
.then(user => {
return dispatch(getUserSuccess(user));
}).catch(error => {
throw error;
});
}
};
export function getUserSuccess(user) {
return {type: USER, payload: user};
}
Also note that you need to have mapStateToProps so it brings the user to your component. Then you can access it using this.props.user within your component. It should be like this.
UserPage.propTypes = {
user: PropTypes.object.isRequired
};
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({getUser}, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);
Finally you may access the user like this.
render() {
const {user} = this.props;
return(
<div>
<div>user.name</div>
</div>
);
}
You need to use componentWillReceiveProps to do that, for example:
componentWillReceiveProps(nextProps) {
if (nextProps.user !== this.state.user) {
this.setState({
user: nextProps.user
});
}
}
now you can use user inside your component.
Here you can find more information.
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.