React native, redux and axios : UnhandledPromiseRejectionWarning - node.js

I have an app where you can create many events, and I have created an API with express and node JS. In the react-native projet, I use Axios to fetch with API. But I have this weird error in my terminal :
(node:59293) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): RangeError: Invalid status code: 0
(node:59293) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
When I try to create an event, and in my browser console I have this :
Error: Network Error
at createError (createError.js:15)
at XMLHttpRequest.handleError (xhr.js:87)
at XMLHttpRequest.dispatchEvent (event-target.js:172)
at XMLHttpRequest.setReadyState (XMLHttpRequest.js:542)
at XMLHttpRequest.__didCompleteResponse (XMLHttpRequest.js:378)
at XMLHttpRequest.js:482
at RCTDeviceEventEmitter.emit (EventEmitter.js:181)
at MessageQueue.__callFunction (MessageQueue.js:242)
at MessageQueue.js:108
at guard (MessageQueue.js:46)
I paste my code here, maybe I'm wrong somewhere,
BACK END PART
import Event from './model';
export const createEvent = async (req, res) => {
const { title, description } = req.body;
const newEvent = new Event({ title, description });
try {
return res.status(201).json({ event: await newEvent.save() });
} catch(e) {
return res.status(e.status).json({ error: true, message: 'Error with event' });
}
};
export const getAllEvents = async (req, res) => {
try {
return res.status(200).json({ events: await Event.find({} )});
} catch(e) {
return res.status(e.status).json({ error: true, message: 'Error with all events' });
}
};
FRONT-END PART
api.js
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000/api';
class EventApi {
constructor() {
this.path = '/events';
}
async fetchEvents() {
try {
const { data } = await axios.get(this.path);
return data.events;
} catch (e) {
console.log(e);
}
}
async createEventPost(args) {
// console.log('nous entrons dans le cadre de la création dun evenement');
try {
console.log('nous tryons');
const res = await axios.post(`${this.path}/new`, { ...args });
console.log(res);
console.log('hello world', args);
return res;
} catch (e) {
console.log(e);
console.log('lol');
}
}
}
export {
EventApi
};
CreateScreen.js
import React, { Component } from 'react';
import { View } from 'react-native';
import { connect } from 'react-redux';
import DateTimePicker from 'react-native-modal-datetime-picker';
import moment from 'moment';
import { Ionicons } from '#expo/vector-icons';
import { createEvent } from './actions';
import { LoadingScreen } from '../../common';
import Colors from '../../../constants/colors';
import styles from './styles/CreateScreen';
import CreateEventsForm from './components/CreateEventsForm';
#connect(
state => ({
event: state.createEvent
}),
{ createEvent }
)
export default class CreateScreen extends Component {
static navigationOptions = {
title: 'Créer un événement',
header: {
style: {
backgroundColor: Colors.whiteColor
},
titleStyle: {
color: Colors.redParty
}
},
tabBar: {
icon: ({ tintColor }) => (
<Ionicons name="ios-create" size={25} color={tintColor} />
)
}
}
state = {
isDateTimePickerVisible: false,
date: moment()
}
_showDateTimePicker = () => this.setState({ isDateTimePickerVisible: true })
_handleDateTimePicker = () => this.setState({ isDateTimePickerVisible: false })
_handleDatePicked = date => {
this.setState({ date });
this._handleDateTimePicker();
}
_checkTitle() {
const { date } = this.state;
if (date > moment()) {
return moment(date).format('MMMM Do YYYY, h:mm.ss a');
}
return 'Choisir une date';
}
_checkNotEmpty() {
const { date } = this.state;
if (date > moment()) {
return false;
}
return true;
}
_createEvent = async values => {
await this.props.createEvent(values);
this.props.navigation.goBack();
}
render() {
const { event } = this.props;
if (event.isLoading) {
return (
<View style={styles.root}>
<LoadingScreen />
</View>
);
} else if (event.error.on) {
return (
<View>
<Text>
{event.error.message}
</Text>
</View>
);
}
return (
<View style={styles.root}>
<CreateEventsForm
createEvent={this._createEvent}
showDateTimePicker={this._showDateTimePicker}
checkTitle={this._checkTitle()}
/>
<DateTimePicker
isVisible={this.state.isDateTimePickerVisible}
onConfirm={this._handleDatePicked}
onCancel={this._handleDateTimePicker}
mode="datetime"
/>
</View>
);
}
}
acion.js
import { EventApi } from '../../../constants/api';
import { fetchMyEvents } from '../home/actions';
const eventApi = new EventApi();
export const CREATE_EVENT = 'CREATE_EVENT';
export const CREATE_EVENT_SUCCESS = 'CREATE_EVENT_SUCCESS';
export const CREATE_EVENT_ERROR = 'CREATE_EVENT_ERROR';
export const createEvent = args => async dispatch => {
dispatch({ type: CREATE_EVENT });
try {
await eventApi.createEventPost(args);
dispatch({ type: CREATE_EVENT_SUCCESS });
} catch (e) {
return dispatch({ type: CREATE_EVENT_ERROR });
}
return await dispatch(fetchMyEvents());
};
reducer.js
import { CREATE_EVENT, CREATE_EVENT_ERROR, CREATE_EVENT_SUCCESS } from './actions';
const INITIAL_STATE = {
error: {
on: false,
message: null
},
isLoading: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CREATE_EVENT:
return {
...INITIAL_STATE,
isLoading: true
};
case CREATE_EVENT_SUCCESS:
return {
...INITIAL_STATE,
isLoading: false
};
case CREATE_EVENT_ERROR:
return {
error: {
on: true,
message: 'Error happened'
},
isLoading: false
};
default:
return state;
}
};
Don't hesitate to ask if you need more code (for example the CreateEventsForm)
Thank you !

It looks like the original error is being swallowed. One solution I found for a similar problem seems to be to drop the version of react-native - https://github.com/mzabriskie/axios/issues/640, but a better approach would to be to discover the original error and then you should be able to address that - https://www.webkj.com/react-native/javascript-error-unhandled-promise-rejection-warning :

Related

Cannot read properties of undefined (reading 'securityService')

I'm trying to make a POST request and am getting this error:
{
"msg": "Unable to read undefined properties (reading 'securityService')"
}
Also, when I'm trying to console.log(this.securityService), I get the same error.
security.controller:
/* eslint-disable no-console */
import { IRouter, Request, Response } from 'express';
import { StatusCodes } from 'http-status-codes';
import SecurityService from './security.service';
class SecurityController {
private securityService: SecurityService;
constructor(private router: IRouter) {
this.router = router;
this.securityService = new SecurityService();
}
public async sendVerificationCode(req: Request, res: Response) {
try {
console.log(this.securityService);
const { receiver } = req.query;
const user = await this.securityService.getUser(String(receiver));
console.log(user);
if (!user) {
return res.status(StatusCodes.CONFLICT).json({ msg: "User with this phone number doesn't exist" });
}
const id = await this.securityService.sendSms;
return res.status(StatusCodes.OK).json({ id });
} catch (error) {
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ msg: error.message });
}
}
}
export default SecurityController;
security.service:
/* eslint-disable no-console */
import { getRepository } from 'typeorm';
import VerificationEntity from '../entities/verification.entity';
class SecurityService {
public async getUser(receiver: string) {
console.log('Check 2');
return getRepository(VerificationEntity).findOne({ mobilePhone: receiver });
}
public async sendSms(body: { receiver: string }) {
const verificationCode = process.env.VERIFICATION_CODE;
await getRepository(VerificationEntity).insert({ mobilePhone: body.receiver, verificationCode });
const { id } = await getRepository(VerificationEntity).findOne();
console.log('Check 3');
return getRepository(VerificationEntity).findOne({ id: id as string });
}
}
export default SecurityService;

POST http://localhost:3000/api/devolucions 400 (Bad Request)

I'm pretty new to javascript, and I'm trying to create a page to return orders for an e-commerce website that I'm creating. I want a customer to be able to start a return when they click the Return Order button on their orders history page. When I click on the Return Order button I'm getting a POST http://localhost:3000/api/devolucions 400 (Bad Request). This post is created in the backend in devolucionRoute.js and called in the frontend in devolucionActions.js. I'm unsure why I am getting this error, and I would really appreciate any help or guidance on how to fix this issue.
Thank you!
Note: devolucion is the name that I used in place of return
devolucionActions.js
import Axios from 'axios';
import {
DEVOLUCION_CREATE_FAIL,
DEVOLUCION_CREATE_REQUEST,
DEVOLUCION_CREATE_SUCCESS,
DEVOLUCION_DETAILS_FAIL,
DEVOLUCION_DETAILS_REQUEST,
DEVOLUCION_DETAILS_SUCCESS,
DEVOLUCION_SUMMARY_REQUEST,
DEVOLUCION_SUMMARY_SUCCESS,
} from '../constants/devolucionConstants';
export const createDevolucion = (devolucion) => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_CREATE_REQUEST, payload: devolucion });
try {
const {
userSignin: { userInfo },
} = getState();
const { data } = await Axios.post('/api/devolucions', devolucion, {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
});
dispatch({ type: DEVOLUCION_CREATE_SUCCESS, payload: data.devolucion });
} catch (error) {
dispatch({
type: DEVOLUCION_CREATE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const detailsDevolucion = (devolucionId) => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_DETAILS_REQUEST, payload: devolucionId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get(`/api/devolucions/${devolucionId}`, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: DEVOLUCION_DETAILS_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: DEVOLUCION_DETAILS_FAIL, payload: message });
}
};
export const summaryDevolucion = () => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_SUMMARY_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get('/api/devolucions/summary', {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: DEVOLUCION_SUMMARY_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: DEVOLUCION_CREATE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
Backend
devolucionRouter.js
import express from 'express';
import expressAsyncHandler from 'express-async-handler';
import Order from '../models/orderModel.js';
import Devolucion from '../models/devolucionModel.js';
import User from '../models/userModel.js';
import Product from '../models/productModel.js';
import {isAdmin, isAuth, isSellerOrAdmin} from '../utils.js';
const devolucionRouter = express.Router();
devolucionRouter.get(
'/mine',
isAuth,
expressAsyncHandler(async (req, res) => {
const devolucion= await Devolucion.find({ user: req.user._id });
res.send(devolucion);
})
);
devolucionRouter.post(
'/',
isAuth,
expressAsyncHandler(async (req, res) => {
if (req.body.devolucionItems.length === 0) {
res.status(400).send({ message: 'No Return' });
} else {
const devolucion = new Devolucion({
seller: req.body.orderItems[0].seller,
orderItems: req.body.orderItems,
devolucionItems: req.body.devolucionItems,
shippingAddress: req.body.shippingAddress,
paymentMethod: req.body.paymentMethod,
itemsPrice: req.body.itemsPrice,
itemsProfit: req.body.itemsProfit,
shippingPrice: req.body.shippingPrice,
taxPrice: req.body.taxPrice,
totalPrice: req.body.totalPrice,
totalProfit: req.body.totalProfit,
user: req.user._id,
});
const createdDevolucion = await devolucion.save();
res
.status(201)
.send({ message: 'New Return Created', devolucion: createdDevolucion });
}
})
);
devolucionRouter.get(
'/:id',
isAuth,
expressAsyncHandler(async (req, res) => {
const devolucion = await Devolucion.findById(req.params.id);
if (devolucion) {
res.send(devolucion);
} else {
res.status(404).send({ message: 'Return Not Found' });
}
})
);
export default devolucionRouter;
server.js
import express from 'express';
import mongoose from 'mongoose';
import dotenv from 'dotenv';
import path from 'path';
import userRouter from './routers/userRouter.js';
import productRouter from './routers/productRouter.js';
import orderRouter from './routers/orderRouter.js';
import devolucionRouter from './routers/devolucionRouter.js';
import uploadRouter from './routers/uploadRouter.js';
dotenv.config();
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
mongoose.connect(process.env.MONGODB_URL || 'mongodb://localhost/AM', {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
});
app.use('/api/uploads', uploadRouter);
app.use('/api/users', userRouter);
app.use('/api/products', productRouter);
app.use('/api/orders', orderRouter);
app.use('/api/devolucions', devolucionRouter);
app.get('/api/config/paypal', (req, res) => {
res.send(process.env.PAYPAL_CLIENT_ID || 'sb');
});
const __dirname = path.resolve();
app.use('/uploads', express.static(path.join(__dirname, '/uploads')));
app.get('/', (req, res) => {
res.send('Server is ready');
});
app.use((err, req, res, next) => {
res.status(500).send({ message: err.message });
});
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Serve at http://localhost:${port}`);
});
OrderHistoryScreen.js (Frontend)
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { listOrderMine, detailsOrder } from '../actions/orderActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {createDevolucion } from '../actions/devolucionActions';
import { DEVOLUCION_CREATE_RESET } from '../constants/devolucionConstants';
export default function OrderHistoryScreen(props) {
const orderId = props.match.params.id;
const order = useSelector((state) => state.order);
const devolucionCreate = useSelector((state) => state.devolucionCreate);
const {success, devolucion } = devolucionCreate;
const orderMineList = useSelector((state) => state.orderMineList);
const { loading, error, orders } = orderMineList;
const dispatch = useDispatch();
useEffect(() => {
dispatch(listOrderMine());
dispatch(detailsOrder(orderId));
}, [dispatch, orderId]);
const placeDevolucionHandler = () => {
dispatch(createDevolucion({ ...order, devolucionItems: order.orderItems }));
};
useEffect(() => {
if (success) {
props.history.push(`/devolucion/${devolucion._id}`);
dispatch({ type: DEVOLUCION_CREATE_RESET });
}
}, [dispatch, devolucion, props.history, success]);
return (
<div>
<h1>Order History</h1>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<table className="table">
<thead>
<tr>
<th>ACTIONS</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order._id}>
<td>
<button
type="button"
onClick={placeDevolucionHandler}
className="small"
// disabled={cart.cartItems.length === 0}
>
Return Order
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
devolucionReducer.js (Frontend)
import {
DEVOLUCION_CREATE_FAIL,
DEVOLUCION_CREATE_REQUEST,
DEVOLUCION_CREATE_RESET,
DEVOLUCION_CREATE_SUCCESS,
DEVOLUCION_DETAILS_FAIL,
DEVOLUCION_DETAILS_REQUEST,
DEVOLUCION_DETAILS_SUCCESS,
DEVOLUCION_DELETE_REQUEST,
DEVOLUCION_DELETE_SUCCESS,
DEVOLUCION_DELETE_FAIL,
DEVOLUCION_DELETE_RESET,
DEVOLUCION_SUMMARY_REQUEST,
DEVOLUCION_SUMMARY_SUCCESS,
DEVOLUCION_SUMMARY_FAIL,
} from '../constants/devolucionConstants';
export const devolucionCreateReducer = (state = {}, action) => {
switch (action.type) {
case DEVOLUCION_CREATE_REQUEST:
return { loading: true };
case DEVOLUCION_CREATE_SUCCESS:
return { loading: false, success: true, devolucion: action.payload };
case DEVOLUCION_CREATE_FAIL:
return { loading: false, error: action.payload };
case DEVOLUCION_CREATE_RESET:
return {};
default:
return state;
}
};
export const devolucionDetailsReducer = (
state = { loading: true },
action
) => {
switch (action.type) {
case DEVOLUCION_DETAILS_REQUEST:
return { loading: true };
case DEVOLUCION_DETAILS_SUCCESS:
return { loading: false, devolucion: action.payload };
case DEVOLUCION_DETAILS_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
export const devolucionDeleteReducer = (state = {}, action) => {
switch (action.type) {
case DEVOLUCION_DELETE_REQUEST:
return { loading: true };
case DEVOLUCION_DELETE_SUCCESS:
return { loading: false, success: true };
case DEVOLUCION_DELETE_FAIL:
return { loading: false, error: action.payload };
case DEVOLUCION_DELETE_RESET:
return {};
default:
return state;
}
};
export const devolucionSummaryReducer = (
state = { loading: true, summary: {} },
action
) => {
switch (action.type) {
case DEVOLUCION_SUMMARY_REQUEST:
return { loading: true };
case DEVOLUCION_SUMMARY_SUCCESS:
return { loading: false, summary: action.payload };
case DEVOLUCION_SUMMARY_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
store.js (frontend)
import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { cartReducer } from './reducers/cartReducers';
import {
devolucionCreateReducer,
devolucionDeleteReducer,
devolucionShipReducer,
devolucionDeliverReducer,
devolucionDetailsReducer,
devolucionListReducer,
devolucionMineListReducer,
devolucionPayReducer,
devolucionSummaryReducer,
} from './reducers/devolucionReducers';
import {
orderCreateReducer,
orderDeleteReducer,
orderShipReducer,
orderDeliverReducer,
orderDetailsReducer,
orderListReducer,
orderMineListReducer,
orderPayReducer,
orderSummaryReducer,
orderReducer,
} from './reducers/orderReducers';
import {
productCategoryListReducer,
productCreateReducer,
productDeleteReducer,
productDetailsReducer,
productListReducer,
productReviewCreateReducer,
productUpdateReducer,
} from './reducers/productReducers';
const initialState = {
userSignin: {
userInfo: localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null,
},
cart: {
cartItems: localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [],
shippingAddress: localStorage.getItem('shippingAddress')
? JSON.parse(localStorage.getItem('shippingAddress'))
: {},
paymentMethod: 'PayPal',
},
};
const reducer = combineReducers({
devolucionCreate: devolucionCreateReducer,
devolucionDetails: devolucionDetailsReducer,
devolucionDelete: devolucionDeleteReducer,
devolucionSummary: devolucionSummaryReducer,
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
order: orderReducer,
userSignin: userSigninReducer,
userRegister: userRegisterReducer,
orderCreate: orderCreateReducer,
orderDetails: orderDetailsReducer,
orderPay: orderPayReducer,
orderMineList: orderMineListReducer,
orderSummary: orderSummaryReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdateProfileReducer,
userUpdate: userUpdateReducer,
productCreate: productCreateReducer,
productUpdate: productUpdateReducer,
productDelete: productDeleteReducer,
orderList: orderListReducer,
orderDelete: orderDeleteReducer,
orderShip: orderShipReducer,
orderDeliver: orderDeliverReducer,
productReviewCreate: productReviewCreateReducer,
userList: userListReducer,
userDelete: userDeleteReducer,
productCategoryList: productCategoryListReducer,
});
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancer(applyMiddleware(thunk))
);
export default store;

Jest - Invariant failed: You should not use <Link> outside a <Router>

I'm getting this error when running a test, however I have wrapped the component I am testing in a <BrowserRouter> component:
● Axios › gets a response
Invariant failed: You should not use <Link> outside a <Router>
91 |
92 | if (RootComponent) {
> 93 | const component = mountWithCustomWrappers(<Wrapper store={store}><RootComponent
{...props} />, rootWrappers);
nock.integration.test
import React from 'react';
import axios from 'axios';
const core = require('tidee-life-core');
import httpAdapter from 'axios/lib/adapters/http';
import doFetch from '../../../helpers/doFetch.js';
import ComponentBuilder from "../component-builder";
import LoginPage from "../../../scenes/login/login-page";
const host = 'http://example.com';
process.env.API_URL = host;
axios.defaults.host = host;
axios.defaults.adapter = httpAdapter;
const makeRequest = () => {
return doFetch({
url: core.urls.auth.login(),
queryParams: { foo: 'bar' },
})
.then(res => res.data)
.catch(error => console.log(error));
};
describe('Axios', () => {
let component;
let componentBuilder;
beforeEach(() => {
componentBuilder = new ComponentBuilder();
});
test('gets a response', async () => {
componentBuilder.includeInterceptor('login');
component = await componentBuilder.build({
RootComponent: LoginPage,
selector: 'LoginForm',
});
return makeRequest()
.then(response => {
expect(typeof response).toEqual('object');
expect(response.data.token).toEqual('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhNDY3MGE3YWI3ZWM0ZjQ2MzM4ODdkMzJkNzRkNTY5OSIsImlhdCI6MTU1MjA5MDI1NX0.vsKLXJEqSUZK-Y6IU9PumfZdW7t1SLM28jzJL89lcrA');
});
});
});
login-page.jsx:
import React, { Component } from 'react';
import PropTypes from "prop-types";
import { withRouter } from 'react-router-dom';
import { injectIntl, intlShape } from 'react-intl';
import queryString from 'query-string';
import { Link } from 'tidee-life-ui';
import LoginForm from './login-form.jsx';
import { doLogin } from '../../api/auth/auth-api';
import Auth from '../../modules/Auth';
import messages from '../../messages';
const { urls } = require('tidee-life-core');
class LoginPage extends Component {
constructor(props) {
super(props);
const { intl } = props;
if (Auth.isUserAuthenticated()) {
props.history.replace({ pathname: urls.pages.pathBoxes() });
}
this.messages = {
'account.activation.error.expired': intl.formatMessage(messages['account.activation.error.expired']),
'account.activation.required': intl.formatMessage(messages['account.activation.required']),
'common.click': intl.formatMessage(messages['common.click']),
'common.here': intl.formatMessage(messages['common.here']),
'error.500.msg': intl.formatMessage(messages['error.500.msg']),
'forgot.success': intl.formatMessage(messages['forgot.success']),
'login.account.needs.activating.partial': intl.formatMessage(messages['login.account.needs.activating.partial']),
'login.error.account.credentials': intl.formatMessage(messages['login.error.account.credentials']),
'login.validation.email': intl.formatMessage(messages['login.validation.email']),
'login.validation.password': intl.formatMessage(messages['login.validation.password']),
'signup.account.created': intl.formatMessage(messages['signup.account.created'])
};
let alertMessage;
let alertMessageType;
const query = queryString.parse(props.location.search);
if ('signup-success' in query) {
alertMessage = this.messages['signup.account.created'];
alertMessageType = 'success';
} else if ('forgot-success' in query) {
alertMessage = this.messages['forgot.success'];
alertMessageType = 'success';
}
this.state = {
alert: {
type: alertMessageType ? alertMessageType : '',
msg: alertMessage ? alertMessage : '',
},
user: {
email: '',
password: ''
}
};
this.changeUser = this.changeUser.bind(this);
this.clearAlert = this.clearAlert.bind(this);
this.processForm = this.processForm.bind(this);
}
clearAlert() {
this.setState({ alert: {
type: '',
msg: '',
}});
}
processForm(e) {
e.preventDefault();
return doLogin({
email: this.state.user.email,
password: this.state.user.password,
}).then((response) => {
Auth.authenticateUser(response.data.token);
this.props.history.replace({ pathname: urls.pages.pathBoxes() });
}).catch((error) => {
const msg = error.message && this.messages[error.message] ? [this.messages[error.message]] : [this.messages['error.500.msg']];
if (error.message === 'account.activation.error.expired' || error.message === 'account.activation.required') {
const to = urls.pages.pathResendLink(error.data.confirmHash);
msg.push(` ${this.messages['common.click']} `);
msg.push(<Link underline color="inherit" key="email" to={to}>{this.messages['common.here']}</Link>);
msg.push(` ${this.messages['login.account.needs.activating.partial']}`);
}
this.setState({
alert: {
type: 'error',
msg,
}
});
});
}
changeUser(event) {
const { name, value } = event.target;
this.setState((currentState) => ({
user: {
...currentState.user,
[name]: value,
}
}));
}
render() {
return (
<LoginForm
data-test="login-form"
alert={this.state.alert}
onSubmit={this.processForm}
onChange={this.changeUser}
user={this.state.user}
/>
);
}
}
LoginPage.propTypes = {
history: PropTypes.object,
intl: intlShape.isRequired,
location: PropTypes.object.isRequired,
};
export default injectIntl(withRouter(LoginPage));
component-builder.js
import React from "react";
import nock from 'nock';
import cloneDeep from 'lodash.clonedeep';
import { mountWithCustomWrappers } from 'enzyme-custom-wrappers';
import { waitForStoreState } from './wait/wait-for-store-state';
import Wrapper from './wrapper.jsx';
import waitForComponentPredicate from './wait-for-component-predicate/wait-for-component-predicate';
import waitForComponentSelector from './wait-for-component-selector/wait-for-component-selector';
import { startAllNockServiceIntercepts } from './nock/nock-manager';
import nockServices from './nock/services';
import store from "../../store/store";
import wrappers from './wrappers';
const rootWrappers = component => wrappers(component);
class ComponentBuilder {
constructor() {
this.nockInterceptors = [];
this.storePreparers = [];
}
includeInterceptor( interceptorName, nockConfigOverride = null ) {
// Maybe need to do a clone deep here if things start breaking!
const clonedNockService = cloneDeep(nockServices[interceptorName]().nockConfig);
const nockService = {
[interceptorName]: {
...clonedNockService,
...(nockConfigOverride || {}),
}
};
this.nockInterceptors.push(nockService);
}
prepareStore( storePreparer ) {
this.storePreparers.push(storePreparer);
}
async waitForStoreToUpdate() {
const promises = this.storePreparers
.map(service => waitForStoreState(service.redux.storeStateToWaitFor, store));
await Promise.all(promises);
}
async runStorePreparers() {
nock.cleanAll();
const interceptors = [];
this.storePreparers.forEach((service) => {
const interceptorName = service.http.interceptor;
const clonedNockService = service.http.interceptor && cloneDeep(nockServices[interceptorName]().nockConfig);
interceptors.push({
[interceptorName]: {
...clonedNockService,
}
});
});
startAllNockServiceIntercepts(interceptors);
this.storePreparers.forEach(service => service.redux.actionToDispatch && store.dispatch(service.redux.actionToDispatch()));
return await this.waitForStoreToUpdate();
}
/**
* Build a component to be tested.
* #param RootComponent
* #param selector {string} - A selector to wait for. CSS selector or name of component.
* #param props {object}
* #param store {object}
* #param predicate {function} - A function that returns true if a condition is met.
* #param predicateMaxTime {number}
* #param predicateInterval {number}
* #returns {Promise<*>}
*/
async build({
RootComponent = null,
selector = '',
props = {},
predicate = null,
predicateMaxTime = 2000,
predicateInterval = 10,
} = {}) {
try {
await this.runStorePreparers();
startAllNockServiceIntercepts(this.nockInterceptors);
if (RootComponent) {
const component = mountWithCustomWrappers(<Wrapper store={store}><RootComponent {...props} /></Wrapper>, rootWrappers);
if (selector) {
await waitForComponentSelector({ selector, rootComponent: component, store });
}
if (predicate) {
await waitForComponentPredicate({
predicate,
rootComponent: component,
store,
maxTime: predicateMaxTime,
interval: predicateInterval,
});
}
return component;
}
} catch(err) {
throw err;
}
}
}
export default ComponentBuilder;
wrapper.jsx
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import ThemeProvider from "../../theme/Theme.jsx";
import LocaleProviderWrapper from "./locale-provider-wrapper.jsx";
const propTypes = {
children: PropTypes.element.isRequired,
store: PropTypes.object.isRequired,
};
class Wrapper extends Component {
getStore() {
return this.props.store;
}
render() {
return (
<Provider store={this.props.store}>
<LocaleProviderWrapper>
<ThemeProvider>
<BrowserRouter>{this.props.children}</BrowserRouter>
</ThemeProvider>
</LocaleProviderWrapper>
</Provider>
);
}
}
Wrapper.propTypes = propTypes;
export default Wrapper;
Exactly as message says, you cannot use Link which doesn't have any parent of type Router. In you processForm function you are building a messaage with Link component which is worng.
if (error.message === 'account.activation.error.expired' || error.message === 'account.activation.required') {
const to = urls.pages.pathResendLink(error.data.confirmHash);
msg.push(` ${this.messages['common.click']} `);
msg.push(<Link underline color="inherit" key="email" to={to}>{this.messages['common.here']}</Link>);
msg.push(` ${this.messages['login.account.needs.activating.partial']}`);
}
You should use a tag to build dynamic link. May be something like:
msg.push(`${this.messages['common.here']}`);

Cannot get the array in json when I put the data in the state

I'm having trouble to get the data of array in JSON.
I send the json file by using node.js and the code is down below.
const express = require("express");
const router = express.Router(); // 라우터 분리
router.get("/api/hello", (req, res) => {
// app 대신 router에 연결
res.json({ express: "hello~" });
});
router.get("/api/beef", (req, res) => {
res.json();
});
module.exports = router; // 모듈로 만드는 부분
And the data which I send with json is like this.
{
"test": "Beef data get Completed",
"name": "beef",
"data": [
{ "productName": "양지", "price": 20000 },
{ "productName": "갈비", "price": 30000 },
{ "productName": "등심", "price": 15000 }
]
}
]
And then I get the data by using fetch, and save it in the state.
I get the right data which I want in the fetch function but when I try to get the data out of the fetch function I keep getting the error like cannot get the property blahblahblah...
And this is the code I did in the client.
import React, { Component } from "react";
const itemList = ["돼지고기", "소고기", "닭&오리", "Sale", "연락처"];
class Contents extends Component {
state = {
parsedJson : "",
};
componentDidMount() {
this._callAPI()
.then(parsedJson=>{
console.log("In the fetch : ",parsedJson[0].data[0])
this.setState({
parsedJson
})
})
.catch(error => {
console.log(error);
this.setState({
...this.state,
isError: true
});
});
}
_callAPI = async () => {
const response = await fetch(`api/${this.props.curPage}`);
const data = response.json();
if (response.status !== 200) throw Error(data.message);
return data;
};
render() {
const stateData = this.state;
console.log("In the render : ",stateData.parsedJson[0].data[0]) <- !!!!!!!!!!!! where error occur
return (
<>
<RenderByItemList
curPage={this.props.curPage}
data={stateData.productData}
/>
</>
);
}
}
On the first render of your component, parsedJson is string, and the stateData.parsedJson[0] returns an empty string, and there is no data property on an empty string, so you get the error.
For solving this problem you have to write an if inside your render method:
import React, { Component } from "react";
const itemList = ["돼지고기", "소고기", "닭&오리", "Sale", "연락처"];
class Contents extends Component {
state = {
parsedJson: [],
error: null,
};
componentDidMount() {
this._callAPI()
.then(res => {
this.setState({
parsedJson: res[0].data, // here we set state the data array
})
})
.catch(error => {
console.log(error);
this.setState({
...this.state,
error // here we set state occurred error
});
});
}
_callAPI = async () => {
const response = await fetch(`api/${this.props.curPage}`);
const data = response.json();
if (response.status !== 200) throw Error(data.message);
return data;
};
render() {
const { parsedJson, error } = this.state;
if (parsedJson && parsedJson.length) {
return <RenderByItemList
curPage={this.props.curPage}
data={parsedJson}
/>
} else {
return !!error || 'loading';
}
}
}
And also it's better to handle loading on your fetch action:
import React, { Component } from "react";
const itemList = ["돼지고기", "소고기", "닭&오리", "Sale", "연락처"];
class Contents extends Component {
state = {
parsedJson: [],
error: null,
loading: false
};
componentDidMount() {
this.setState({
loading: true,
}, () => {
this._callAPI()
.then(res => {
this.setState({
parsedJson: res[0].data, // here we set state the data array
loading: false,
error: null,
})
})
.catch(error => {
console.log(error);
this.setState({
loading: false,
error // here we set state occurred error
});
});
})
}
_callAPI = async () => {
const response = await fetch(`api/${this.props.curPage}`);
const data = response.json();
if (response.status !== 200) throw Error(data.message);
return data;
};
render() {
const { parsedJson, error, loading } = this.state;
if (loading) {
return 'loading' // or write a Loading component and render it everywhere
} else if (error) {
return error // or write an Error component
} else {
return (
<RenderByItemList
curPage={this.props.curPage}
data={parsedJson}
/>
);
}
}
}

Action creator is being dispatched but the new state is not showing in store

I have written a server and wired up React with Redux, also made use of container-componenent-seperation. The first action fetchSnus() is successfully dispatched, the selectors also seem to work, but for some reason all the actions that I call after the first rendering, such as fetchSnu() (that only fetches ONE single snu-object) e.g. is not accessible for me in the store, meaning: after mapping state and dispatch to props not accessible for me under this.props. I really don't understand why!
You can see the whole project here: https://github.com/julibi/shonagon
This is my container ReadSingleSnuContainer.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchSnus, fetchSnu, setToRead, createSnu, getSnusMatchingKeyword } from '../../actions/index';
import { unreadSnus, randomFirstSnu } from '../../selectors/index';
import ReadSingleSnu from './ReadSingleSnu';
class ReadSingleSnuContainer extends Component {
componentWillMount() {
this.props.fetchSnus();
}
render() {
return (
<ReadSingleSnu { ...this.props } />
);
}
}
const mapStateToProps = (state) => {
return {
snus: state.snus,
randomFirstSnu: randomFirstSnu(state),
candidate: state.candidate
};
}
const mapDispatchToProps = (dispatch) => {
return {
fetchSnus: () => dispatch(fetchSnus()),
getSnusMatchingKeyword,
fetchSnu,
setToRead,
createSnu
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ReadSingleSnuContainer);
This is my component ReadSingleSnu.js:
import React, { Component } from 'react';
import style from './ReadSingleSnu.css'
import Typist from 'react-typist';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
export default class ReadSingleSnu extends Component {
constructor(props) {
super(props);
this.state = { showTitles: false };
}
renderRandomFirstSnu() {
const { randomFirstSnu } = this.props;
const { showTitles } = this.state;
// preselect a keyword
// dispatch the action that searches for other keywords (action I)
if(randomFirstSnu) {
return (
<div>
<h3><Typist cursor={ { show: false } }>{ randomFirstSnu.title }</Typist></h3>
<ReactCSSTransitionGroup
transitionName="snu"
transitionAppear={ true }
transitionAppearTimeout={ 1000 }
transitionEnter={ false }
transitionLeave={ false }
>
<p>{ randomFirstSnu.text }</p>
</ReactCSSTransitionGroup>
{ !showTitles ? (
<div>
<button>Some other</button>
<button onClick={ () => this.handleDoneReading(randomFirstSnu) }>Done reading, next</button>
</div>
) : (
<ReactCSSTransitionGroup
transitionName="keywords"
transitionAppear={ true }
transitionAppearTimeout={ 1000 }
transitionEnter={ false }
transitionLeave={ false }
>
<ul>{ randomFirstSnu.keywords.map((keyword, idx) =>
<li key={ idx }>
<button onClick={ () => this.fetchNextSnu(randomFirstSnu) }>
{ keyword }
</button>
</li>) }
</ul>
</ReactCSSTransitionGroup>
)
}
</div>
);
}
return <div>Loading ...</div>
}
handleDoneReading(snu) {
const { setToRead, getSnusMatchingKeyword } = this.props;
const id = snu._id;
if (snu.keywords.length > 0 && setToRead) {
// setToRead(id, snu);
this.setState({ showTitles: true });
const randomIndex = Math.floor(Math.random() * snu.keywords.length);
const randomKeyword = snu.keywords[randomIndex];
console.log('This is the randomKeyword :', randomKeyword);
getSnusMatchingKeyword(randomKeyword);
} else {
console.log('Here will soon be the select random next snu action :)');
}
}
render() {
console.log('ReadSingleSnu, this.props: ', this.props);
return (
<div className={style.App}>
<div>{ this.renderRandomFirstSnu() }</div>
</div>
);
}
}
This is my actions file:
import axios from 'axios';
export const FETCH_SNUS = 'FETCH_SNUS';
export const FETCH_SNU = 'FETCH_SNU';
export const SET_TO_READ = 'SET_TO_READ';
export const CREATE_SNU = 'CREATE_SNU';
export function getSnusMatchingKeyword(keyword) {
const request = axios.get(`/snus/keyword/${keyword}`);
return {
type: GET_SNUS_MATCHING_KEYWORD,
payload: request
};
}
export function fetchSnus() {
const request = axios.get('/snus');
return {
type: FETCH_SNUS,
payload: request
};
}
export function fetchSnu(id) {
const request = axios.get(`/snus/${id}`);
return {
type: FETCH_SNU,
payload: request
};
}
export function setToRead(id, snu) {
const request = axios.patch(`/snus/${id}`, { title: snu.title, text: snu.text, keywords: snu.keywords, read: true });
return {
type: SET_TO_READ,
payload: request
}
}
export function createSnu(object) {
const request = axios.post('/snus', object);
return {
type: CREATE_SNU,
payload: request
};
}
A Reducer:
import { FETCH_SNUS, FETCH_SNU, SET_TO_READ, CREATE_SNU } from '../actions/index';
export default function(state = [], action) {
switch(action.type) {
case FETCH_SNUS:
return [ ...state, ...action.payload.data ];
case FETCH_SNU:
return [ ...state, ...action.payload.data ];
case SET_TO_READ:
return [ ...state, ...action.payload.data ];
case CREATE_SNU:
return [...state, ...action.payload.data ];
default:
return state;
}
}
I tested all endpoints via Postman and they work. So that should not be the problem… Please help! I cannot find a solution to this problem.

Resources