React does not recognize defined function - javascript

The problem occurs when I do try to fire onClick function linked with the ButtonWithDate component ( it is being inherited from the parent component ):
<Button
variant="extendedFab"
onClick={this.props.updateDateAndHour}
color="primary">
Display Date
</Button>
Once I use it, the following error occurs:
TypeError: _services_API__WEBPACK_IMPORTED_MODULE_7__.default.getResponse is not a function
Bind of updateDateAndHour func:
<ButtonWithDate updateDateAndHour={this.updateDateAndHour}></ButtonWithDate></center>
App.js
import React, { Component } from 'react';
import API from './services/api';
import ButtonWithDate from './components/ButtonWithDate';
class App extends Component {
constructor() {
super();
this.state= {
'day': '',
'month': '',
'year': ''
};
this.API = API;
}
updateDateAndHour = () => {
console.log(this);
var self = this;
API.getResponse().then((res) => {
var local_date = res.date.split('-');
self.setState({
day: local_date[0]
})
self.setState({
day: local_date[1]
})
self.setState({
day: local_date[2]
})
});
}
render() {
return (
<div>
<center>
<ButtonWithDate updateDateAndHour={this.updateDateAndHour}></ButtonWithDate></center>
</div>
);
}
}
export default App;
services/api.js
import axios from 'axios';
const URL = 'https://...';
export default class API{
getResponse() {
axios.get(URL)
.then(result => {
return result
})
.catch(error => {
return null;
});
}
};
components/ButtonWithDate.js
import React from 'react';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
export default class ButtonWithDate extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="button-container">
<Button
variant="extendedFab"
onClick={this.props.updateDateAndHour}
color="primary">
Display Date
</Button>
</div>
)
}
}
ButtonWithDate.propTypes = {
onClickButton: PropTypes.func
}
When I do use a console.log along with the function name, I can easily access the content of it:
updateDateAndHour = () => {
console.log(getResponse);
getResponse()...
but once I invoke getResponse(), an exception takes a place.
Screenshot of the error:

first of all sounds like there is a problem with this binding;
class App extends Component {
constructor() {
...
this.updateDateAndHour = this.updateDateAndHour.bind(this);
}
updateDateAndHour(){
...
})
...
}
you can use babel proposal class properties to automatically to those bindings for you instead of manually doing every time
checkout this link for further reading;
However the main problem is in your ./api/ it's simpler to export getResponse() like below
services/api.js
import axios from 'axios';
const URL = 'https://...';
const getResponse = () => {
axios.get(URL)
.then(result => {
return result
})
.catch(error => {
return null;
});
}
export default getResponse;
EDIT:
for the sake the debugging cut getResponse() body in your /api/ and use it in updateDateAndHour(){ ... }) to see if it's working or not ( to see if the problem is in importing or the function itself )

Ok, the issue was connected with with usage of
.then((res) => {
var local_date = res.date.split('-');
self.setState({
day: local_date[0]
})
getResponse() function was not returning a new Promis object. That was the problem.
Quick-fix:
api.js
import axios from 'axios';
const DATE_JSON_URL = 'https://...';
function getResponse() {
return new Promise((resolve, reject) => {
axios.get(URL)
.then(result => {
resolve(result);
})
.catch(error => {
reject(error);
})
})
}
export default getResponse;

Related

Async Action Redux Unhandled Rejection (Error): Actions must be plain objects. Use custom middleware for async actions

I have a react project that is using redux-thunk. I created an action that will hit an endpoint, then set store to data received. Currently, I am using .then but when I call the action in the componentdidmount, the data is not there. The component renders before the data is available. To fix this, I decided to turn my action into an async action and then await in my componentdidmount. The problem is, as soon as I put async in my action, I get this error....
Unhandled Rejection (Error): Actions must be plain objects. Use custom middleware for async actions.
Here is my code
Action
export const getCasesSuccess = async (data) => {
return {
type: GET_ALL_CASES,
data
}
};
export const getAllCases = () => {
return (dispatch) => {
axios.get('https://corona.lmao.ninja/all')
.then(res => {
const cases = res.data
dispatch(getCasesSuccess(cases))
})
.catch(error => {
throw(error)
})
}
}
Component where action is called
import React from "react";
import { connect } from "react-redux";
import { getAllCases } from "../../store/actions/index";
import AllCases from '../../components/allcases/allCases';
class DataContainer extends React.Component {
constructor(props) {
super(props);
this.state = { }
}
componentDidMount = async () => {
await this.props.getAllCases()
}
render() {
return (
<div>
<AllCases allCases={this.props.allCases} />
</div>
);
}
}
const mapStateToProps = (state) => (
{
allCases: state.allCases
}
)
const mapDispatchToProps = dispatch => {
return {
getAllCases: () => dispatch(getAllCases()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DataContainer);
Remove the async from componentDidmount and use the async and await in getAllCases method
export const getAllCases = async () => {
return (dispatch) => {
await axios.get('https://corona.lmao.ninja/all')
.then(res => {
const cases = res.data
dispatch(getCasesSuccess(cases))
})
.catch(error => {
throw(error)
})
}
}
As the error messages says, Redux actions must be plain objects. Since you're using thunk middleware, you can dispatch functions. But you're returning a promise. Since the data loading is asynchronous, your component should check if the data exists and if it doesn't, render a loading indicator or something. In your reducer, you can set a default state for allCases to null which the DataContainer component will use when the component mounts.
export const getCasesSuccess = (data) => {
return {
type: GET_ALL_CASES,
data
}
};
import React from "react";
import { connect } from "react-redux";
import { getAllCases } from "../../store/actions/index";
import AllCases from '../../components/allcases/allCases';
class DataContainer extends React.Component {
componentDidMount() {
this.props.getAllCases()
}
render() {
const { allCases } = this.props
if (!allCases) {
return <div>Loading...</div>
}
return (
<div>
<AllCases allCases={this.props.allCases} />
</div>
);
}
}
const mapStateToProps = (state) => ({
allCases: state.allCases
})
const mapDispatchToProps = {
getAllCases,
}
export default connect(mapStateToProps, mapDispatchToProps)(DataContainer);

How to export multiple functions ES6

I'm working in a vue project, I'm very new to vue.
We have a db_handler.js in out /src/utility folder.
It looks like this:
import fakeApiCall from "./mock";
import axios from "axios";
import { DEBUG_MODE, API_ENDPOINT } from "./namespaces";
function fetchData(method, slug, payload) {
//axios.defaults.headers.withCredentials = true;
//return (!DEBUG_MODE) ? axios[method](`${API_ENDPOINT}${slug}`, payload) : fakeApiCall(slug);
return axios[method](`${API_ENDPOINT}${slug}`, payload);
/*var url = "http://localhost:8080" + slug
return axios({
method: method,
url: url,
headers: {
'Authorization': payload
}
});*/
}
function sendData(method, slug, payload) {
axios[method](`${API_ENDPOINT}${slug}`, payload);
}
export default fetchData
What I need to know:
How can I export my sendData()?
They used a short syntax so far because they only exported one function.
How can I export multiple functions? I also want the names to remain "fetchData" and "sendData"
EDIT:
I tried to apply the approaches of Iamhuynq and Bergi, but now something goes south. I am importing the functions first and foremost in
moduleUser.js and authUser.js which reside in /src/store/modules.
The authUser.js is used for the identification of the user, so of course it is used in the login screen. When I now try to login, I get "Type Error: Object undefined". I guess this is because the functions returning the server response are somehow failing or not found.
The codebase connected to this behavior is the Login screen, the db_handler which Ive already shown you and a module called "moduleAuth.js".
First, the login screen looks like this:
<template>
<div>
<h1>Login</h1>
<p>Name:</p>
<div class="inputbox">
<input ref="username" type='text' v-on:keydown.enter="userLogin">
</div>
<p>Password:</p>
<div class="inputbox">
<input class="inputbox" ref="password" type='password' v-on:keydown.enter="userLogin">
</div>
<p>{{error}}</p>
<button v-on:click='userLogin'>Login</button>
</div>
</template>
<script>
import store from "../store/store";
import { AUTH_REQUEST } from "../store/actions/auth";
export default {
data () {
return {
error: ""
}
},
methods: {
userLogin: function(){
this.error = '';
store.dispatch(AUTH_REQUEST,{username: this.$refs.username.value, password: this.$refs.password.value})
.then((res) => {
this.$router.push({path: '/profile'});
})
.catch((err) => {
this.error = err;
});
this.$refs.password.value = '';
}
}
}
</script>
<style>
.inputbox{
width: 25%;
}
</style>
moduleAuth.js, from which the AUTH_REQUEST vue-action is coming, looks like this:
import axios from "axios";
import Vue from 'vue';
import Vuex from 'vuex';
import {fetchData, sendData} from "../../utility/db_handler";
import { USER_REQUEST } from "../actions/user";
import { AUTH_REQUEST, AUTH_LOGOUT, AUTH_FAIL, AUTH_SUCCESS } from "../actions/auth";
import { METHOD_POST, JWT } from "../../utility/namespaces";
Vue.use(Vuex);
const storeAuth = {
state: {
token: localStorage.getItem(JWT) || '',
loginState: ''
},
getters: {
isAuthenticated: state => !!state.token,
getLoginState: state => state.loginState
},
mutations: {
[AUTH_REQUEST]: (state) => {
state.loginState = 'pending';
},
[AUTH_FAIL]: (state) => {
state.loginState = 'error';
},
[AUTH_SUCCESS]: (state, mToken) => {
state.loginState = '';
state.token = mToken;
},
[AUTH_LOGOUT]: (state) => {
return new Promise ((resolve, reject) =>{
state.loginState = '';
state.token = '';
localStorage.removeItem(JWT);
resolve();
//Catch?
})
}
},
actions: {
[AUTH_REQUEST]: ({ commit, dispatch }, uObj) => {
return new Promise((resolve, reject) => {
commit(AUTH_REQUEST);
fetchData(METHOD_POST, '/login',{
username: uObj.username,
password: uObj.password
}).then(function (res) {
commit(AUTH_SUCCESS,res.headers.authorization);
localStorage.setItem(JWT,res.headers.authorization);
axios.defaults.headers.common['Authorization'] = res.headers.authorization;
dispatch(USER_REQUEST);
resolve(res.data);
}).catch(function(err) {
commit(AUTH_FAIL);
reject(err);
})
})
},
[AUTH_LOGOUT]: ({ commit}) => {
commit(AUTH_LOGOUT);
}
}
}
export default storeAuth
Now, if just roll back the changes to the export/import sections, everything works. So the problem should definitely be connected to this.
you can use export
export function sendData() {...}
and you can import like this
import fetchData, { sendData } from '/src/utility/db_handler.js;'
Here my suggestion is, if you are exporting more then one function, you should use export method instead of export default. It will make your code more readable and ll use for future debugging.
export function function1(params) {
.......
}
export function function2() {
......
}
Here there is a two way to import functions
by using import { function1, function2} from "./exportedFunctionFile" make sure you are using same function name as you exported!
other method is use * as yourVariableName example import * as myFunctions from "./exportedFunctionFile" this would use when you are exporting too many functions now you can use your imported functions as myfunctions.function1()
if you want to export using default key word, export functions as object example export default {function1,function2} and you could use it like import * as myFunctions from "./exportedFunctionFile" which is similar as a second way of importion.
Hope it will Help you
export the functions in an object
export default {
sendData: sendData,
fetchData: fetchData
}
then to use
import * as DBHandler from '#/src/utility/db_handler'
...
DBHandler.sendData()
On the function files
func1(params) {
...
}
func2(params) {
...
}
export default default {
function1: function1,
function2: function2
}
On the other file
import * as _ from './module address'
then
_.default.func1.call(args)
_.default.func2.call(args)

React Redux not dispatching API Call to delete player

I am trying to migrate my previously working local state to redux. Now loading available Players works just fine, but deleting will somehow stop in the playerActions.js file, where I dispatch and then return an API Call. So to further give details here are my code parts in relevance:
PlayerPage.js (Component):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadPlayers, deletePlayer } from '../../redux/actions/playerActions';
import PlayerForm from './playerform';
import PlayCard from './playercard';
import PropTypes from 'prop-types';
import { toast } from 'react-toastify';
class PlayerPage extends Component {
constructor(props) {
super(props);
this.handleDeletePlayer = this.handleDeletePlayer.bind(this);
state = {};
componentDidMount() {
const players = this.props;
players.loadPlayers().catch(err => {
alert('Loading players failed. ' + err);
});
}
handleDeletePlayer = player => {
toast.success('Player deleted');
try {
deletePlayer(player);
} catch (err) {
toast.error('Delete failed. ' + err.message, { autoClose: false });
}
};
render() {
const styles = {
margin: '20px'
};
return (
<div className="container-fluid">
<div>
<h2 style={styles}>Add Player</h2>
<div className="container-fluid">
<PlayerForm handleAddNewPlayer={this.handleAddPlayer} />
</div>
</div>
<hr></hr>
<div>
<h2 style={styles}>Available Player</h2>
<div className="container-fluid">
{this.props.players.map(player => (
<PlayCard
player={player}
key={player.id}
imageSource={`${process.env.API_URL}/${player.profileImg}`}
onDeletePlayer={this.handleDeletePlayer}
/>
))}
</div>
</div>
</div>
);
}
}
PlayerPage.propTypes = {
players: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
players: state.players
};
}
const mapDispatchToProps = {
loadPlayers,
deletePlayer
};
export default connect(mapStateToProps, mapDispatchToProps)(PlayerPage);
And the Action being called is in here:
playerActions.js:
import * as types from './actionTypes';
import * as playerApi from '../../api/playerApi';
export function loadPlayersSuccess(players) {
return { type: types.LOAD_PLAYERS_SUCCESS, players };
}
export function deletePlayerOptimistic(player) {
return { type: types.DELETE_PLAYER_OPTIMISTIC, player };
}
export function loadPlayers() {
return function(dispatch) {
return playerApi
.getAllPlayers()
.then(players => {
dispatch(loadPlayersSuccess(players));
})
.catch(err => {
throw err;
});
};
}
export function deletePlayer(player) {
console.log('Hitting deletePlayer function in playerActions');
return function(dispatch) {
dispatch(deletePlayerOptimistic(player));
return playerApi.deletePlayer(player);
};
}
The console.log is the last thing the app is hitting. But the API Call is never made though.
API Call would be:
playerApi.js:
import { handleResponse, handleError } from './apiUtils';
const axios = require('axios');
export function getAllPlayers() {
return (
axios
.get(`${process.env.API_URL}/player`)
.then(handleResponse)
.catch(handleError)
);
}
export function deletePlayer(id) {
return (
axios
.delete(`${process.env.API_URL}/player/${id}`)
.then(handleResponse)
.catch(handleError)
);
}
I was like spraying out console.log in different places and files and the last one I am hitting is the one in playerActions.js. But after hitting it the part with return function(dispatch) {} will not be executed.
So if someone could point me in a general direction I'd be more than grateful.
It looks like you are calling your action creator deletePlayer but you aren't dispatching it correctly. This is why the console.log is being called but not the method that does the request.
I'd recommend taking a look at the documentation for mapDispatchToProps to fully understand how this works. In your example, you should just need to change the call to deletePlayer in your PlayerPage component to this.props.deletePlayer() to use the action creator after it's been bound to dispatch properly.
this how the mapDispatchToProps should be:
const mapDispatchToProps = dispatch => {
return {
load: () => dispatch(loadPlayers()),
delete: () => dispatch(deletePlayer()),
}
}
then call load players with this.props.load() and delete player with this.props.delete()

Jest wait until typemoq function called

So I have a mock (typemoq) http call that I'm passing into my react component (mounted with enzyme):
const mockhttp: TypeMoq.IMock<IHttpRequest> = TypeMoq.Mock.ofType<IHttpRequest>();
mockhttp
.setup(x => x.get('/get-all-goal-questions'))
.returns(() => {
return Promise.resolve(mockResponse.object.data);
});
const wrapper = mount(<Goals history={Object} http={mockhttp.object} />);
expect(wrapper.find('#foo')).to.have.lengthOf(1);
However, the mock "Get" isn't being called until after the expected, how can I get the expect to wait until the mock is called to test?
// Edit here is the code under test
let httpCall = this.props.pageHoc.httpRequest -- the call im mocking
import React, { Component } from 'react';
import { Row, Col } from 'react-bootstrap';
import { Animated } from "react-animated-css";
import { Answer, IPageHOC } from '../../interfaces/pageObjects';
// Fonts
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faCheck } from '#fortawesome/free-solid-svg-icons'
// Cookies
import cookie from 'react-cookies';
// Google analytics
import ReactGA from 'react-ga';
type GoalsComponent = {
answers: Answer[],
showError:boolean,
showAnimation:boolean,
question:string,
questionId:number
};
type Props = {
history:any,
pageHoc?: IPageHOC
}
export default class Goals extends Component<Props, GoalsComponent>
{
constructor(props: any) {
super(props);
this.state = {
answers : [],
showError: false,
showAnimation:false,
question: "",
questionId: 0
}
}
componentDidMount(){
// Hide nav
this.props.pageHoc.hideRightNav();
this.loadQuestions();
}
loadQuestions(){
// Setup auth
let auth = this.props.pageHoc.externalAuth;
auth.setToken(cookie.load('Email'), cookie.load('Password')).then((x) => {
let httpCall = this.props.pageHoc.httpRequest;
// Headers
httpCall.setHeaders({
Organization: cookie.load('Organization')
});
httpCall.get(`/thrive/goal/get-all-goal-questions`)
.then((x) => {
this.setState({
answers:x.data.goalQuestions[0].answers,
question: x.data.goalQuestions[0].question,
questionId: x.data.goalQuestions[0].id
});
})
.catch((x) => {
console.log(x, "error");
});
});
}
render() {
return (
<ul className="list-group list-group-goals">
{this.state.answers.map((x:Answer) =>
<li className={("list-group-item ") + (x.selected ? "selected" : "")} key={x.id} onClick={() => this.toggleGoal(x.id)}>
{x.answer}
<FontAwesomeIcon icon={faCheck} className={("goal-tick ") + (x.selected ? "goal-tick-red" : "")} />
</li>
)}
</ul>
);
}
}
hmm if you are trying to test async request you should follow what is written here:
https://jestjs.io/docs/en/tutorial-async
for the short version your test should look something like this:
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});
You can do something like this:
fun test = async () => {
const mockhttp: TypeMoq.IMock<IHttpRequest> = TypeMoq.Mock.ofType<IHttpRequest>();
mockhttp
.setup(x => x.get('/get-all-goal-questions'))
.returns(() => {
return Promise.resolve(mockResponse.object.data);
});
const wrapper = await mount(<Goals history={Object} http={mockhttp.object} />);
expect(wrapper.find('#foo')).to.have.lengthOf(1);
}
This will wait for the promise returned by the mocked get function to resolve and the component to render with the latest data.

ReactJS: Can I separate a function that changes state in a separate component?

I've got a function in my React app that is calling in componentWillMount lifecycle method.
It grabs data from JSON file and push it to the component's state (it is a text data, I later insert that text into the page).
I'm going to use the same function on many other components, can I separate this function into a separate component to make it reusable?
Here is my code:
import React from 'react';
import axios from 'axios';
import logo from '../img/company_logo.png';
import '../css/header.scss';
import getTextData from './getTextData';
const NumberList = (props) => {
console.log(props.value);
const itemList = props.value;
const listItems = itemList.map(number => (
<li key={number.toString()}>
{number}
</li>
));
return (
<ul>{listItems}</ul>
);
};
export default class Header extends React.Component {
constructor() {
super();
this.state = {};
}
componentWillMount() {
axios.get('./data.json')
.then((res) => {
this.setState({
siteData: res.data,
});
})
.catch((err) => {
console.log(err);
});
}
render() {
// console.log(this.state);
const { siteData } = this.state;
if (siteData) {
console.log(siteData.data.mainPage.navBar);
} else {
return null;
}
return (
<div className="headerWrapper">
<img src={logo} alt="company_logo" id="companyLogo" />
<NumberList value={siteData.data.mainPage.navBar} />
</div>
);
}
}
Yes, create a function and return the repsonse-data, must use async await
//fetchService.js
import axios from 'axios';
export default async function fetchService(){
let responseData = [];
await axios.get('./data.json')
.then((res) => {
responseData = res.data;
})
.catch((err) => {
console.log(err);
});
return responseData;
}
// App.js
import fetchService from './fetchService';
async componentDidMount() {
let tempData = await fetchService();
this.setState({
siteData: tempData,
});
}
i hope this helps!

Categories