How to display data from api in react component - node.js

I am using axios to get data from an API and the retrieved data was successfully dispatched to the store, I currently have some problems in displaying the data in my React component.
I was able to console.log the data successfully but it turns out my component renders before the data was fully retrieved from the API.
Below is the component to display the data:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import HeaderSidebar from './includes/header-navbar';
import AllBooks from './includes/all-books';
import { getAllBooks } from '../../actions/book_actions';
class AdminHome extends Component {
componentDidMount() {
this.props.actions.getAllBooks();
}
renderBooks(){
const allbooks = this.props.books;
return allbooks.map((book) => {
return (<div key={book.id}>
<AllBooks title={book.title}
description={book.description}
/>
</div>)
})
}
render() {
return (
<div >
<HeaderSidebar />
{this.renderBooks()}
})}
})}
</div >
)
}
}
function mapStateToProps(state) {
return {
books: state.book.data
}
}
AdminHome.PropTypes = {
books: PropTypes.object.isRequired
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getAllBooks
}, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminHome);
Actions
export function getAllBooks() {
return dispatch => axios.get(`${API_URL}`)
.then((res) => {
dispatch({
type: GET_ALL_BOOKS,
data: res.data
});
return res.data;
})
.catch(error => error);
}
Reducers
import { ADD_BOOK,
GET_ALL_BOOKS
} from '../actions/types';
const INITIAL_STATE = {};
function bookReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_BOOK:
return { ...state, message: 'Book added Successfully' };
case GET_ALL_BOOKS:
return { ...state, data: action.data };
default:
return state;
}
}
export default bookReducer;

You can show something else until the data is done loading
renderBooks(){
if(!this.props.books) {
return (<p>Loading data..<p/>);
}
// rest of your code here
Or you can initialize the property with an empty array so that at least nothing crashes until the data is done loading.
function mapStateToProps(state) {
return {
books: state.book.data || [] // maybe have to extra checks here if state.book doesn't exist yet
}
(Or do both, and change if(!this.props.books) to if(this.props.books.length < 1).

Related

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']}`);

jest Warning: Can't perform a React state update on an unmounted component. on componentDidMount

I am trying to test a fetch call in componentDidMount. trying different solutions but always hit a dead end. currently i have
component
import React, { Component } from 'react';
import Seatmap from 'react-seatmap';
import '../../style/main.scss';
import seats from '../../seatData';
import Ticket from '../Ticket';
class Booking extends Component {
constructor(props){
super(props);
this.state = {
row: null,
number: null,
data: [],
selected: null,
isLoaded: false
}
this.addSeatCallback = this.addSeatCallback.bind(this);
this.removeSeatCallback = this.removeSeatCallback.bind(this);
}
controller = new AbortController();
async fetchData() {
await fetch(window.location.protocol + window.location.hostname,{
signal: this.controller.signal
})
.then(res => seats)
.then(
(result) => {
this.setState({
isLoaded: true,
data: this.formatSeatData(result)
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}
async componentDidMount(){
await this.fetchData();
}
componentWillUnmount() {
this.controller.abort();
}
getIndexFromLetter(letter) {
return letter.charCodeAt(0) - 65;
}
formatSeatData(seats){
const rows = [];
seats.seats.map( (seat,i) => {
const arr = seat.seatNumber.split(/([0-9]+)/).filter(Boolean);
seat.number = arr[0];
seat.isReserved = !seat.available;
const index = this.getIndexFromLetter(arr[1]);
if(typeof rows[index] === 'undefined') {
rows[index] = [];
}
rows[index].push(seat);
return true;
});
return rows;
}
addSeatCallback(row, number) {
const { data } = this.state;
const index = this.getIndexFromLetter(row);
this.setState({ row, number, selected: data[index][number-1] });
}
removeSeatCallback(row, number) {
this.setState({row: null, number: null, selected: null});
}
render() {
const { data, selected } = this.state;
if(Object.keys(data).length>0){
return (
<div className="container-fluid text-center">
<div className="" style={{float:'left', padding: '10px', marginTop:'10px'}}>
<Seatmap rows={data} maxReservableSeats={1} alpha addSeatCallback={this.addSeatCallback} removeSeatCallback={this.removeSeatCallback} />
</div>
<div className="" style={{float:'left', padding: '10px'}}>
<Ticket selected={selected} />
</div>
</div>
);
}
return null;
}
}
export default Booking;
test
import React from 'react';
import ReactDOM from 'react-dom';
import Booking from '../../src/components/Booking';
import seats from '../../src/seatData';
import { shallow } from 'enzyme';
describe('Booking', () => {
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Booking />, div);
ReactDOM.unmountComponentAtNode(div);
});
let wrapper;
beforeEach(() => {
wrapper = shallow(<Booking />, { disableLifecycleMethods: true });
});
afterEach(() => {
wrapper.unmount();
});
it("must render a loading span before api call success", () => {
// expect(wrapper.find("span.loading").length).toBe(1);
});
it("componentDidMount", (done) => {
const spyDidMount = jest.spyOn(Booking.prototype,"componentDidMount");
fetch.mockImplementation(() => {
return Promise.resolve({
status: 200,
json: () => {
return Promise.resolve({
seats:{}
});
}
});
});
const didMount = wrapper.instance().componentDidMount();
expect(spyDidMount).toHaveBeenCalled();
didMount.then(() => {
wrapper.update();
expect(wrapper.find("div.seatmap_container"));
expect(wrapper.find("spans.loading").length).toBe(0);
spyDidMount.mockRestore();
fetch.mockClear();
done();
});
});
});
here is my repo https://github.com/shorif2000/elucidat-javascript-test
error
PASS test/components/Booking.test.js
Booking
✓ renders without crashing (29ms)
✓ must render a loading span before api call success (1ms)
✓ componentDidMount (14ms)
console.error node_modules/react-dom/cjs/react-dom.development.js:545
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Booking
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.145s
Ran all test suites matching /test\/components\/Booking.test.js/i.

TypeError: Cannot read property 'map' of undefined in reactjs

I'm working on an app with expressjs and reactjs. I fetched the data from the backend using expressjs but I get map is not a function.
import React, { Component } from "react";
import "./products.css";
import Listofproducts from "./listofproducts";
import { Link } from "react-router-dom";
class products extends Component {
constructor(props) {
super(props);
this.state = {
productInfo: ""
};
}
async getProducts() {
try {
const data = await fetch("http://localhost:4000/product");
const jsonData = await data.json();
this.setState({
productInfo: jsonData
});
console.log(this.state.productInfo);
} catch (error) {
console.log(error);
}
}
componentDidMount() {
this.getProducts();
}
render() {
return (
<React.Fragment>
<Listofproducts itemlists={this.state.productInfo} />
</React.Fragment>
);
}
}
export default products;
Here is the component productLists where I sent the props to work with it.
import React, { Component } from "react";
import Product from "./products";
class Listofproducts extends Component {
render() {
const { itemslist } = this.props;
console.log(itemslist);
// console.log is working here i get back the data on the console
return itemslist.map(list => {
console.log(list);
});
}
}
export default Listofproducts;
You set productInfo to an empty string in the constructor of products, and strings don't have a map method.
Change the default value to an empty array instead and it will work just as well before and after your network request completes.
class Products extends Component {
constructor(props) {
super(props);
this.state = {
productInfo: []
};
}
// ...
}

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.

React native, redux and axios : UnhandledPromiseRejectionWarning

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 :

Categories

Resources