jest test case for input change and button in reactjs - reactjs

I am very new to jest so i don't know how to proceed with jest. I have write test case using jest for input on change and button click for the below component. But it failed. issue with 'Method “props” is only meant to be run on a single node. 0 found instead.' please help me
code:
import React from 'react';
import { Col } from 'react-bootstrap';
class FITB extends React.Component {
constructor(props) {
super(props);
this.String = String;
this.defaultTextSize = 8;
this.state = {
inputList: [],
splitList: [],
count: 0,
isValid: false
};
this.onInputChange = this.onInputChange.bind(this);
this.onClickSend = this.onClickSend.bind(this);
this.checkInputValidations = this.checkInputValidations.bind(this);
}
componentDidMount() {
this.initialize();
}
onInputChange(index) {
return (event) => {
const inputList = this.state.inputList;
let isValid = true;
inputList[index] = event.target.value;
if (!this.isValidInput(inputList[index])) {
isValid = false;
}
this.setState({
inputList,
isValid
});
// console.log('onInputChange fib state', this.state);
};
}
onClickSend() {
const {
splitList,
inputList,
count
} = this.state;
// console.log('onClickSend fib before state', this.state);
const onReply = this.props.onReply;
let fullText = '';
splitList.map((text, index) => {
fullText += text;
if ((index < count - 1) && inputList[index]) {
fullText += inputList[index];
}
return true;
});
if (onReply) {
onReply(fullText);
}
// console.log('onClickSend fib after state', this.state);
}
isValidInput(text) {
const regex = /^[\u0020-\u007e]*$/;
const replaceChar160RegExp = new RegExp(this.String.fromCharCode(160), 'g');
return regex.test(text.replace(replaceChar160RegExp, ' '));
}
initialize() {
let text = '';
this.props.messages.map((element) => {
if (element.type && (typeof element.type === 'string') && (element.type === 'FILL_IN_THE_BLANK')) {
text = element.message;
}
// console.log('inside fib', text);
return text;
});
const splitList = text.split(/_+/g);
this.setState({
splitList,
count: splitList.length
});
// console.log('init fib state', this.state);
}
checkInputValidations() {
const {
inputList,
count,
isValid
} = this.state;
let i;
let flag = false;
for (i = 0; i < count - 1; i += 1) {
if (!inputList[i] || inputList[i].trim() === '') {
flag = true;
}
}
// console.log('checkInputValidations', this.state);
return flag || !isValid;
}
render() {
const {
splitList,
count,
inputList
} = this.state;
// console.log('reder fitb', this.state);
return (
<Col lg={12} className="rply-block">
<Col lg={11} className="textarea-block">
<div className="fitb-wrap">
{ splitList && splitList.map((item, index) => (
<span>
<span className="fitb-text">{item}</span>
{ (index < count - 1) && (
<input
className="fitb-input"
type="text"
maxLength="40"
size={(inputList[index] && inputList[index].length > this.defaultTextSize && inputList[index].length) || this.defaultTextSize}
value={inputList[index]}
onChange={this.onInputChange(index)}
autoFocus={index === 0}
aria-describedby={count > 1 ? `Missing word ${index + 1} of ${count - 1}` : 'Missing word'}
aria-label="Fill in missing words"
/>
)}
</span>
))}
</div>
</Col>
<Col lg={1} className="">
<button
className="btn-info-dm"
role="button"
tabIndex="0"
onClick={this.onClickSend}
disabled={this.checkInputValidations()}
>Send</button>
</Col>
</Col>
);
}
}
FITB.propTypes = {
onReply: React.PropTypes.isRequired,
messages: React.PropTypes.array
};
export default FITB;
test files for the input and button click
import React from 'react';
import FITB from '../components/dialogManager/fitb';
import { shallow } from 'enzyme';
import renderer from 'react-test-renderer';
describe('FITB', () => {
let component;
const mockFn = jest.fn();
beforeEach(() => {
component = shallow(<FITB onReply={mockFn} />);
});
test('Should initialize the FITB content', () => {
expect(component.find('.rply-block')).toHaveLength(1);
});
test('Should have been called send', () => {
component.find('.btn-info-dm').simulate('click');
expect(mockFn).toHaveBeenCalled();
});
test('Should render the text box', () => {
expect(component.state().inputList).toEqual([]);
expect(component.state().isValid).toEqual(false);
// expect(component.find('input.fitb-input')).toBeDefined();
console.log('state== ', component.state());
console.log('input == ', component.find('.fitb-input'));
component.find('.fitb-input').simulate('change', { target: { value: 'Qualitative data includes detailed interviews, direct _____, and historical records.' } });
console.log('fitb-input onchange== ', component.find('.fitb-input'));
expect(component.state().inputList).toEqual('Qualitative data includes detailed interviews, direct _____, and historical records.');
expect(component.state().isValid).toEqual(true);
});
// test('Should check the input label', () => {
// const expectedFitbTextLabel = 'Fill in missing words';
// const fitbTextList = [];
// console.log('span fitb txt== ', component.find('.fitb-text').instance().label);
// component.find('.fitb-text').map((elem) => {
// fitbTextList.push(elem.text().trim());
// });
// expect(component.find('.fitb-text').instance().label).toEqual(expectedFitbTextLabel);
// });
test('Should render the fitbText ', () => {
const expectedFitbText = 'Qualitative data includes detailed interviews, direct _____, and historical records.';
const fitbTextList = [];
console.log('span fitb txt== ', component.find('.fitb-text').text());
// fitbTextList.push(expectedFitbText.split(/_+/g));
// console.log('fitbTextList= ', fitbTextList);
component.find('.fitb-text').map((elem) => {
fitbTextList.push(elem.text().trim());
});
expect(fitbTextList).toEqual(expectedFitbText);
});
test('Should check the fitbText ', () => {
const expectedFitbText = 'Qualitative data includes detailed interviews, direct _____, and historical records.';
const fitbTextList = [];
fitbTextList.push(expectedFitbText.split(/_+/g));
expect(component.state().inputList).toEqual([]);
console.log('input list init== ', component.state().inputList);
component.find('input.fitb-input').simulate('change', { target: { value: 'test' } });
console.log('input list== ', component.state().inputList);
// component.find('input').instance().onInputChange(0);
// expect(component.state().inputList).toEqual('test');
});
});
for the another component on button click to send the details
import React from 'react';
class ReplyButtons extends React.Component {
constructor(props) {
super(props);
this.replyBtnWrap = '';
this.replyBtnList = '';
this.state = {
list: [],
isLeftArrowEnabled: false,
isRightArrowEnabled: false
};
this.onClickLeftArrow = this.onClickLeftArrow.bind(this);
this.onClickRightArrow = this.onClickRightArrow.bind(this);
this.onClickReplyBtn = this.onClickReplyBtn.bind(this);
}
componentDidMount() {
this.initializeList();
}
componentDidUpdate() {
this.checkElementOffset();
}
onClickRightArrow() {
const wrapElement = this.replyBtnWrap;
const listElement = this.replyBtnList;
const wrapWidth = wrapElement.offsetWidth;
const listWidth = listElement.offsetWidth;
let listLeft = wrapElement.scrollLeft;
let listOverflowWidth = 0;
listLeft += 400;
listOverflowWidth = listWidth - listLeft;
if (listOverflowWidth < 0) {
listLeft = listWidth - wrapWidth;
}
wrapElement.scrollLeft = listLeft;
this.checkElementOffset();
}
onClickLeftArrow() {
const wrapElement = this.replyBtnWrap;
let listLeft = wrapElement.scrollLeft;
listLeft -= 400;
if (listLeft < 0) {
listLeft = 0;
}
wrapElement.scrollLeft = listLeft;
this.checkElementOffset();
}
onClickReplyBtn(item) {
return () => {
const onReply = this.props.onReply;
if (onReply) {
onReply(item);
}
};
}
checkElementOffset() {
const wrapElement = this.replyBtnWrap;
const listElement = this.replyBtnList;
const wrapWidth = wrapElement.offsetWidth;
const listWidth = listElement.offsetWidth;
const listLeft = wrapElement.scrollLeft;
let listOverflowWidth = 0;
let isLeftArrowEnabled = false;
let isRightArrowEnabled = false;
if (listLeft > 0) {
isLeftArrowEnabled = true;
}
listOverflowWidth = listWidth - listLeft - wrapWidth;
if (listOverflowWidth > 0) {
isRightArrowEnabled = true;
}
if (this.state.isLeftArrowEnabled !== isLeftArrowEnabled || this.state.isRightArrowEnabled !== isRightArrowEnabled) {
this.setState({
isLeftArrowEnabled,
isRightArrowEnabled
});
}
}
initializeList() {
// this.setState({
// list: [{
// type: 'MENU_ITEM',
// text: 'what is quantitative research?',
// return_value: 'what is quantitative research?'
// }, {
// type: 'MENU_ITEM',
// text: 'what is mixed method research?',
// return_value: 'what is mixed method research?'
// }, {
// type: 'MENU_ITEM',
// text: 'what is qualitative research?',
// return_value: 'what is qualitative research?'
// }, {
// type: 'MENU_ITEM',
// text: 'I had a different question',
// return_value: 'I had a different question'
// }, {
// type: 'MENU_ITEM',
// text: 'That was actually my answer',
// return_value: 'That was actually my answer'
// }]
// });
const replyButtonText = [];
// console.log('reply btns props = ', this.props);
if (this.props.messages) {
this.props.messages.map((element) => {
if (element.type && (typeof element.type === 'string') && (element.type === 'MENU_ITEM')) {
replyButtonText.push(element);
}
return this.setState({ list: replyButtonText });
});
}
}
render() {
const btnList = this.state.list;
const {
isLeftArrowEnabled,
isRightArrowEnabled
} = this.state;
return (
<div className="r-wrap">
{ isLeftArrowEnabled && (
<button className="r-btn-left-arrow" onClick={this.onClickLeftArrow} role="button" tabIndex="0">
<i className="glyphicon glyphicon-menu-left" />
</button>
)}
<div className="r-btn-wrap" ref={(e) => { this.replyBtnWrap = e; }}>
<div className="r-btn-list" ref={(e) => { this.replyBtnList = e; }}>
{
btnList && btnList.map(btnItem => <button
className="r-btn"
role="button"
tabIndex="0"
onClick={this.onClickReplyBtn(btnItem)}
title={btnItem.text}
>{btnItem.text}</button>)
}
</div>
</div>
{ isRightArrowEnabled && (
<button className="r-btn-right-arrow" onClick={this.onClickRightArrow} role="button" tabIndex="0">
<i className="glyphicon glyphicon-menu-right" />
</button>
)}
</div>
);
}
}
ReplyButtons.propTypes = {
onReply: React.PropTypes.isRequired,
messages: React.PropTypes.array
};
export default ReplyButtons;
test file:
import React from 'react';
import ReplyButtons from '../components/dialogManager/replybuttons';
import { shallow } from 'enzyme';
import renderer from 'react-test-renderer';
const mockOutputObj = [{
type: 'MENU_ITEM',
text: 'what is quantitative research?',
return_value: 'what is quantitative research?'
}, {
type: 'MENU_ITEM',
text: 'what is mixed method research?',
return_value: 'what is mixed method research?'
}];
describe('ReplyButtons', () => {
let component;
const mockFn = jest.fn();
beforeEach(() => {
component = shallow(<ReplyButtons onReply={mockFn} />);
});
test('Should initialize the ReplyButtons content', () => {
expect(component.find('.r-wrap')).toHaveLength(1);
});
test('Should check the ReplyButtons click', () => {
console.log('r-btn==> ', component.find('button.r-btn'));
component.find('.r-btn').simulate('click');
expect(mockFn).toHaveBeenCalled();
});
});
Please help me.

I think the error happens because the selector in the last test case
component.find('button.r-btn')
refers to buttons that are build by iterating on this.state.list in the ReplyButtons component
AND you do not provide the prop 'messages' that would trigger a feeding to the list
try instanciating ReplyButtons with some messages like
beforeEach(() => {
component = shallow(<ReplyButtons onReply={mockFn} messages={['1','2']}/>);
});
and it should be ok (btw not having the error stack is pretty unconfortable here, there might be other issues)

Related

React.js error: The service worker navigation preload request was cancelled before 'preloadResponse' settled

I have an issue with my React application (with Redux Saga), I'm getting the console error:
The service worker navigation preload request was cancelled before 'preloadResponse' settled. If you intend to use 'preloadResponse', use waitUntil() or respondWith() to wait for the promise to settle.
I see this error on the console only on Chrome, not in Firefox or Edge.
This error does not affect my application.
The following steps reproduce the error:
1. Main page upload.
2. Go to movie details page.
3. Go back to main page.
Main.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { mainActions } from '../../store/actions/actions';
import './Main.scss';
import { MoviesList, SearchPanel } from '../../components';
const propTypes = {};
const defaultProps = {};
class Main extends Component {
constructor(props) {
super(props);
this.handleSearchTextChange = this.handleSearchTextChange.bind(this);
this.handleLoadMoreButtonClick = this.handleLoadMoreButtonClick.bind(this);
this.handleMovieClick = this.handleMovieClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
const { moviesList } = this.props;
if (!moviesList || moviesList.length <= 0) {
this.getMovies(null, false);
}
}
handleLoadMoreButtonClick() {
this.getMovies(null, false);
}
handleMovieClick(e) {
if (e.target.className === 'movie') {
this.props.history.push(`/details/${e.currentTarget.dataset.id}`);
}
}
handleSearchTextChange(e) {
const { pageNumber, favoriteMoviesList } = this.props;
this.props.onSearchTextChange({
searchText: e.target.value,
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList
});
}
handleFavoriteMovieClick(e) {
const { id, name, posterId } = e.currentTarget.dataset;
const { moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: id, name: name, posterId: posterId },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
}
getMovies(updatedSearchText, isSearchChange) {
const { searchText, pageNumber, favoriteMoviesList } = this.props;
this.props.onLoadMovies({
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList,
updatedSearchText: isSearchChange ? updatedSearchText : searchText,
isSearchChange: isSearchChange
});
}
render() {
const { searchText, isLoadingMoreMovies, isPager, moviesList } = this.props;
return (
<div className="main-area">
<SearchPanel
searchText={searchText}
onSearchTextChange={this.handleSearchTextChange}
/>
<MoviesList
pageName='movies'
moviesList={moviesList}
isLoadingMoreMovies={isLoadingMoreMovies}
isPager={isPager}
onLoadMoreClick={this.handleLoadMoreButtonClick}
onMovieClick={this.handleMovieClick}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
</div>
);
}
}
Main.propTypes = propTypes;
Main.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
searchText: state.main.searchText,
pageNumber: state.main.pageNumber,
isLoadingMoreMovies: state.main.isLoadingMoreMovies,
isPager: state.main.isPager,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovies: (request) => dispatch(mainActions.loadMovies(request)),
onSearchTextChange: (request) => dispatch(mainActions.searchTextChange(request)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
Details.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { detailsActions, mainActions } from '../../store/actions/actions';
import './Details.scss';
import { ActorsList, ButtonClick, CrewsList, FeaturesList, PageTitle, ProductionsList, Rating, Trailer } from '../../components';
import movieUtils from '../../utils/movie.utils';
const propTypes = {};
const defaultProps = {};
class Details extends Component {
constructor(props) {
super(props);
this.handleBackClick = this.handleBackClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
this.isFavorite = false;
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
if (this.props.moviesList.length <= 0) {
this.handleBackClick();
return;
}
const movieId = this.props.match.params.id;
if (!movieId) {
this.handleBackClick();
return;
}
this.props.onLoadMovieDetails(movieId);
this.updateIsFavorite(movieId);
}
numberWithCommas(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
updateIsFavorite(movieId) {
this.isFavorite = this.props.favoriteMoviesList.findIndex(movie => parseInt(movie.id) === parseInt(movieId)) > -1;
}
handleBackClick() {
this.props.history.push(`/`);
}
handleFavoriteMovieClick() {
const { movie, moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: movie.id, name: movie.title, posterId: movie.poster_path },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
this.updateIsFavorite(movie.id);
}
render() {
const { movie, youtubeKey, credits } = this.props;
if (!movie) {
return null;
}
const { adult, poster_path, budget, genres, homepage, imdb_id, original_language, original_title,
overview, popularity, production_companies, production_countries, release_date, revenue, runtime, spoken_languages,
status, tagline, title, video, vote_average, vote_count } = movie;
const genresText = genres.map(genre => genre.name).join(', ');
const countriesText = production_countries.map(country => country.name).join(', ');
const languagesText = spoken_languages.map(language => language.name).join(', ');
const featuresList = [
{ item: 'Release Date', value: release_date },
{ item: 'Budget', value: `$${this.numberWithCommas(budget)}` },
{ item: 'Revenue', value: `$${this.numberWithCommas(revenue)}` },
{ item: 'Length', value: `${runtime} minutes` },
{ item: 'Popularity', value: popularity },
{ item: 'Original Title', value: original_title },
{ item: 'For Adults', value: adult ? 'Yes' : 'No' },
{ item: 'Original Language', value: original_language },
{ item: 'Spoken Languages', value: languagesText },
{ item: 'Countries', value: countriesText },
{ item: 'Status', value: status },
{ item: 'Is Video', value: video ? 'Yes' : 'No' }
];
const linksList = [];
if (homepage) {
linksList.push({ id: 1, name: 'Homepage', url: homepage });
}
if (imdb_id) {
linksList.push({ id: 2, name: 'IMDB', url: `https://www.imdb.com/title/${imdb_id}` });
}
const actorsList = movieUtils.removeDuplicates(credits ? credits.cast ? credits.cast : null : null, 'name');
const crewsList = movieUtils.removeDuplicates(credits ? credits.crew ? credits.crew : null : null, 'name');
return (
<div>
<section className="details-area">
<PageTitle
pageName='details'
pageTitle='Details'
/>
<ul className="details-content">
<li className="details-left" style={{ backgroundImage: `url('https://image.tmdb.org/t/p/original${poster_path}')` }}></li>
<li className="details-right">
<h2>{title} ({release_date.substr(0, 4)})</h2>
<p className="genres">{genresText}</p>
<p className="description short">{tagline}</p>
<Rating
rating={vote_average}
votesCount={this.numberWithCommas(vote_count)}
/>
<p className="description full">{overview}</p>
<div className="extra">
<FeaturesList
featuresList={featuresList.slice(0, 5)}
linksList={null}
isFavorite={this.isFavorite}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
{youtubeKey && <Trailer
youtubeKey={youtubeKey}
/>}
</div>
</li>
<div className="extra-features">
<FeaturesList
featuresList={featuresList.slice(5, featuresList.length)}
linksList={linksList}
isFavorite={null}
onFavoriteMovieClick={null}
/>
<ProductionsList
productionsList={production_companies}
/>
</div>
</ul>
</section>
<section className="actors-area">
<PageTitle
pageName='actors'
pageTitle='Cast'
/>
<ActorsList
actorsList={actorsList}
/>
</section>
<section className="crew-area">
<PageTitle
pageName='crew'
pageTitle='Crew'
/>
<CrewsList
crewsList={crewsList}
/>
</section>
<ButtonClick
buttonText={'Back'}
buttonTitle={'Back'}
isLoading={false}
onClick={this.handleBackClick}
/>
</div>
);
}
}
Details.propTypes = propTypes;
Details.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
movie: state.details.movie,
youtubeKey: state.details.youtubeKey,
credits: state.details.credits,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovieDetails: (movieId) => dispatch(detailsActions.loadDetails(movieId)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Details);
What I already looked in:
Getting The service worker navigation preload request was cancelled before 'preloadResponse' settled
https://learn.microsoft.com/en-us/answers/questions/108004/getting-the-service-worker-navigation-preload-requ.html
https://support.google.com/mail/thread/4055804?hl=en
https://love2dev.com/pwa/service-worker-preload/
I tried to put this on Details.jsx page, but it didn't work:
self.addEventListener('fetch', event => {
event.respondWith(async function () {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse; // Else, use the preloaded response, if it's there
const response = await event.preloadResponse;
if (response) return response; // Else try the network.
return fetch(event.request);
}());
});
self.addEventListener('activate', event => {
event.waitUntil(async function () {
// Feature-detect
if (self.registration.navigationPreload) { // Enable navigation preloads!
console.log('Enable navigation preloads!');
await self.registration.navigationPreload.enable();
} return;
})();
});
How can I solve this issue? Thanks.
Had same error, even my iframe wasn't loading..whatever video you are using from youtube use nocookie/embed in url. It's working for me.
Try changing https://www.youtube.com/watch?v=i8eBBG46H8A to
https://www.youtube-nocookie.com/embed/i8eBBG46H8A
Hope nocookie & embed helps..!!

React-Select upgraded from 1.x to 3.0.4 - Selected value not shown

I am a bit new in React and i am trying to upgrading react from old version to 16.9.
Everything works great but the following component that i had to upgrade to version 3.0.4.
The problem is that when i select a value, it's selected but i don't see the selected option on the screen and i see only the default placeholder.
i found few answers that explaining that i have to set the value using the full object and as per my understanding i am doing so.
Can someone help me to find the problem?
// #flow
import React, { Component } from "react";
import ReactSelect from "react-select";
import { isNil } from "lodash";
import type { FormProps } from "redux-form";
import cx from "classnames";
import { getClassName, addModifierTo } from "ui-kit/utils/component";
import { injectIntl, FormattedMessage } from "react-intl";
/* import "react-select/dist/react-select.css"; */
import "./Select.scss";
type Option = {
label: string,
value: string
}
type Props = {
name?: string,
options?: Array<Option>,
className?: string
} & FormProps;
type State = {
selectedOption: Object
}
const getInitOption = (multi, valueType) => (multi && valueType === "Array" ? [] : "");
class Select extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
selectedOption: this.getOptionFromPropValue(),
openUp: false
};
}
componentDidMount() {
const { defaultValue, input } = this.props;
if (input && defaultValue) {
input.onChange(defaultValue);
}
}
getOptionFromPropValue(props: Props = this.props) {
const {
defaultValue,
input,
multi,
valueType = "String",
value: valueFromProp
} = props;
let value = "";
if (!isNil(defaultValue)) {
value = defaultValue;
}
if (input && !isNil(input.value)) {
({ value } = input);
}
else if (valueFromProp) {
value = valueFromProp;
}
let options;
if (multi) {
if (valueType === "String" || typeof value === "string") {
value = value.split(",");
}
options = value.map(valueItem => ({ value: valueItem }));
} else {
options = [{ value }];
}
if (props.options) {
options = options.map((option) => {
const sameOption = props.options.find(item => item.value === option.value);
if (sameOption) {
return sameOption;
}
return option.value !== "" ? option : null;
});
}
return multi ? options : options[0];
}
initialized: false
UNSAFE_componentWillReceiveProps(nextProps) {
const {
loadedLabel, input, multi, valueType
} = nextProps;
if (!this.initialized) {
this.setState({ selectedOption: this.getOptionFromPropValue(nextProps) });
this.initialized = true;
}
if (this.selectedOption && !this.selectedOption.label && loadedLabel) {
this.setState(prevState => ({ selectedOption: { ...prevState.selectedOption, label: loadedLabel } }));
}
if (input && !input.value) {
this.setState({ selectedOption: getInitOption(multi, valueType) });
}
}
render() {
const {
className,
name,
label,
simple,
creatable = false,
asyncLoad = false,
input,
hasBorder = true,
errorOffset = false,
meta = {},
onChange: onChangeEvent,
onChangeReplace,
initialized,
defaultValue,
multi,
valueType = "String",
maxHeight,
...props
} = this.props;
const msg = meta.error || meta.warning;
let Tag;
if (asyncLoad) {
Tag = creatable ? ReactSelect.AsyncCreatable : ReactSelect.Async;
} else {
Tag = creatable ? ReactSelect.Creatable : ReactSelect;
}
const onChange = onChangeReplace || ((selectedOption) => {
if (!selectedOption) {
selectedOption = { value: "" };
}
let value;
if (multi && Array.isArray(selectedOption)) {
value = selectedOption.map(({ value: valueItem }) => valueItem);
if (valueType === "String") {
value = value.join(",");
}
} else {
({ value } = selectedOption);
}
if (input) {
input.onChange(value);
}
if (onChangeEvent) onChangeEvent({ target: { name, value } });
this.setState({ selectedOption });
});
// for fix bug with losing value on Blur (https://github.com/erikras/redux-form/issues/1185)
const onBlur = () => input && this.state.selectedOption && input.onBlur(this.state.selectedOption.value);
const onOpenHandler = () => {
if (!isNil(this.elDOM)) {
let inputEl = this.elDOM.input;
if (!isNil(inputEl) && !(inputEl instanceof window.Element)) {
inputEl = inputEl.input;
}
if (!isNil(inputEl) && !isNil(this.elDOM.menuContainer)) {
const rect = inputEl.getBoundingClientRect();
const menuRect = this.elDOM.menuContainer.getBoundingClientRect();
const bottomDistance = window.innerHeight - rect.bottom;
const openUp = bottomDistance < (menuRect.height + 20);
this.setState({ openUp }, () => {
if (openUp && rect.top < menuRect.height) {
window.scrollTo(0, window.scrollY - (menuRect.height - rect.top));
}
});
}
}
};
const ccn = getClassName("Select");
const modify = addModifierTo(ccn());
const modifyWrap = addModifierTo(ccn("wrap"));
console.log(this.state.selectedOption);
// className "Select" is already present in Tag
return (
<div
className={cx(
ccn("wrap"),
errorOffset && modifyWrap("error-offset"),
className,
meta.visited && meta.invalid && modify("invalid"),
this.state.openUp && modify("up")
)}
>
{label && <div className={ccn("label")}>{label}</div>}
<Tag
className={cx(
simple && modify("simple"),
!hasBorder && modify("no-border"),
)}
style={{ maxHeight }}
name={name}
multi={multi}
{...input}
clearable={false}
backspaceRemoves={false}
autosize={simple || false}
value={this.state.selectedOption}
onChange={onChange}
onBlur={onBlur.bind(this)}
onOpen={onOpenHandler}
ref={(el) => { this.elDOM = el; }}
placeholder=""
noResultsText={<FormattedMessage id="select.noResultsText" defaultMessage="No results found" />}
{...props}
/>
{meta.visited && msg && <div className={ccn("validationMsg")}>{msg}</div>}
</div>
);
}
}
export default injectIntl(Select);
I think the react version for React-Select is 16.8.
https://github.com/JedWatson/react-select/issues/3585

Can't submit react form

I have a method to submit the edited form, but when I'm clicking submit button, nothing happens. Via the console I figured out, that eventIndex = -1. What should I fix in the code below?
editEvent(event) {
const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
const {
events } = this.state;
let onlyDate = false;
console.log('event', event);
console.log('calendarEvents', calendarEvents);
const idx = events.indexOf(event);
const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
const editedEvent = { ...event };
console.log('Event Index', eventIndex);
const nextEvents = [...events];
nextEvents.splice(idx, 1, editedEvent);
if (eventIndex !== -1) {
const item = calendarEvents.details.items[eventIndex];
if (item.start.date && item.end.date) {
editedEvent.start = moment(event.start).format('YYYY-MM-DD');
editedEvent.end = moment(event.end).format('YYYY-MM-DD');
onlyDate = true;
}
}
this.setState({
events: nextEvents,
}, () => {
console.log('Object', { id: event.event.id, title: event.formValues.title, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
editGoogleCalendarEvent({
id: event.event.id,
start: moment(event.event.start).hours(event.formValues.period === 'AM' ? event.formValues.hour % 12 : (event.formValues.hour % 12) + 12).minutes(event.formValues.minute).toISOString(),
end: moment(event.event.end).hours(event.formValues.period === 'AM' ? event.formValues.hour % 12 : (event.formValues.hour % 12) + 12).minutes(event.formValues.minute).toISOString(),
title: event.formValues.title,
userId: currentUser.id,
timezone: currentUser.timezone,
onlyDate,
});
});
}
Form:
<EditCalendarEventForm
show={this.state.editShow}
isSubmitting={editEventProcess.isSubmitting}
calendarEvent={this.state.calendarEvent}
onSubmit={this.editEvent}
onHide={this.hideEditEventModal}
/>
Here is the whole page, maybe you will understand the situation better with it. Also I have an EditFormPage and api to work with the requests.
I was using moveEvent as an example to create editEvent method.
import React, { PropTypes } from 'react';
import moment from 'moment-timezone';
import Helmet from 'react-helmet';
import _ from 'lodash';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { GoogleLogin, GoogleLogout } from 'react-google-login';
import { reduxForm, reset } from 'redux-form';
import BigCalendar from 'react-big-calendar';
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';
import 'react-big-calendar/lib/less/styles.less';
import 'react-big-calendar/lib/addons/dragAndDrop/styles.less';
import AddCalendarEventForm from '../../../app/components/AddCalendarEventForm';
import EditCalendarEventForm from '../../../app/components/EditCalendarEventForm';
import { translate } from '../../../common/utilities/localization';
import {
selectCurrentUser,
selectCurrentGoogleUser,
} from '../../containers/App/selectors';
import {
submitGoogleAuth,
fetchGoogleCalendarEvents,
editGoogleCalendarEvent,
addGoogleCalendarEvent,
} from './actions';
import {
selectGoogleAuth,
selectCalendarEvents,
selectAddEventProcess,
selectEditEventProcess,
} from './selectors';
const formName = 'addCalendarEvent';
const formNameEdit = 'editCalendarEvent';
const DragAndDropCalendar = withDragAndDrop(BigCalendar);
const localizer = BigCalendar.momentLocalizer(moment);
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser(),
currentGoogleUser: selectCurrentGoogleUser(),
googleAuth: selectGoogleAuth(),
calendarEvents: selectCalendarEvents(),
addEventProcess: selectAddEventProcess(),
editEventProcess: selectEditEventProcess(),
});
const mapDispatchToProps = (dispatch) => ({
submitGoogleAuth: (externalUserId, googleToken) => dispatch(submitGoogleAuth(externalUserId, googleToken)),
fetchGoogleCalendarEvents: (data) => dispatch(fetchGoogleCalendarEvents(data)),
editGoogleCalendarEvent: (data) => dispatch(editGoogleCalendarEvent(data)),
addGoogleCalendarEvent: (data) => dispatch(addGoogleCalendarEvent(data)),
resetForm: () => dispatch(reset(formName)),
resetEditForm: () => dispatch(reset(formNameEdit)),
});
#reduxForm({
form: formName,
})
#connect(mapStateToProps, mapDispatchToProps)
export default class CalendarPage extends React.Component {
static propTypes = {
currentUser: PropTypes.any,
currentGoogleUser: PropTypes.any,
submitGoogleAuth: PropTypes.func.isRequired,
googleAuth: PropTypes.object,
fetchGoogleCalendarEvents: PropTypes.func,
calendarEvents: PropTypes.object,
editGoogleCalendarEvent: PropTypes.func,
addGoogleCalendarEvent: PropTypes.func,
addEventProcess: PropTypes.object,
editEventProcess: PropTypes.object,
resetForm: PropTypes.func,
resetEditForm: PropTypes.func,
};
constructor(props) {
super(props);
this.state = {
events: [],
show: null,
calendarEvent: null,
editShow: null,
};
this.onSuccess = this.onSuccess.bind(this);
this.onFailure = this.onFailure.bind(this);
this.moveEvent = this.moveEvent.bind(this);
this.editEvent = this.editEvent.bind(this);
this.newEvent = this.newEvent.bind(this);
this.showEventModal = this.showEventModal.bind(this);
this.showEditEventModal = this.showEditEventModal.bind(this);
this.hideEventModal = this.hideEventModal.bind(this);
this.hideEditEventModal = this.hideEditEventModal.bind(this);
}
componentDidMount() {
const { currentUser, currentGoogleUser } = this.props;
if (currentGoogleUser && currentGoogleUser.expires_at && moment(currentGoogleUser.expires_at).isAfter(moment())) {
this.props.fetchGoogleCalendarEvents({ ...currentGoogleUser, userId: currentUser.id });
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.currentGoogleUser !== this.props.currentGoogleUser) {
this.props.fetchGoogleCalendarEvents({ ...nextProps.currentGoogleUser, userId: nextProps.currentUser.id });
}
if (nextProps.calendarEvents && nextProps.calendarEvents.details) {
const events = [];
for (const item of nextProps.calendarEvents.details.items) {
if (item.start && item.end) {
events.push({
id: item.id,
title: item.summary,
start: moment(item.start.dateTime || item.start.date),
end: moment(item.end.dateTime || item.end.date),
});
}
}
this.setState({ events });
}
if (!nextProps.addEventProcess.isSubmitting && this.props.addEventProcess.isSubmitting) {
this.hideEventModal();
}
if (!nextProps.editEventProcess.isSubmitting && this.props.editEventProcess.isSubmitting) {
this.hideEventModal();
}
}
onSuccess(ev) {
const { submitGoogleAuth, currentUser } = this.props;
submitGoogleAuth(currentUser.id, { ...ev.tokenObj, profileEmail: ev.profileObj.email });
}
onFailure(ev) {
console.log('onFailure', ev);
}
moveEvent({ event, start, end, isAllDay: droppedOnAllDaySlot }) {
const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
const { events } = this.state;
let onlyDate = false;
const idx = events.indexOf(event);
const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
let allDay = event.allDay;
if (!event.allDay && droppedOnAllDaySlot) {
allDay = true;
} else if (event.allDay && !droppedOnAllDaySlot) {
allDay = false;
}
const updatedEvent = { ...event, start, end, allDay };
const nextEvents = [...events];
nextEvents.splice(idx, 1, updatedEvent);
if (eventIndex !== -1) {
const item = calendarEvents.details.items[eventIndex];
if (item.start.date && item.end.date) {
updatedEvent.start = moment(start).format('YYYY-MM-DD');
updatedEvent.end = moment(end).format('YYYY-MM-DD');
onlyDate = true;
}
}
this.setState({
events: nextEvents,
}, () => {
editGoogleCalendarEvent({ ...updatedEvent, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
});
}
editEvent(event) {
const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
const { events } = this.state;
let onlyDate = false;
console.log('event', event);
console.log('calendarEvents', calendarEvents);
const idx = events.indexOf(event);
const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
const editedEvent = { ...event };
console.log('Event Index', eventIndex);
const nextEvents = [...events];
nextEvents.splice(idx, 1, editedEvent);
if (eventIndex !== -1) {
const item = calendarEvents.details.items[eventIndex];
if (item.start.date && item.end.date) {
editedEvent.start = moment(event.start).format('YYYY-MM-DD');
editedEvent.end = moment(event.end).format('YYYY-MM-DD');
onlyDate = true;
}
}
this.setState({
events: nextEvents,
}, () => {
console.log('Object', { id: event.event.id, title: event.formValues.title, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
editGoogleCalendarEvent({
id: event.event.id,
start: moment(event.event.start).hours(event.formValues.period === 'AM' ? event.formValues.hour % 12 : (event.formValues.hour % 12) + 12).minutes(event.formValues.minute).toISOString(),
end: moment(event.event.end).hours(event.formValues.period === 'AM' ? event.formValues.hour % 12 : (event.formValues.hour % 12) + 12).minutes(event.formValues.minute).toISOString(),
title: event.formValues.title,
userId: currentUser.id,
timezone: currentUser.timezone,
onlyDate,
});
});
}
resizeEvent = ({ event, start, end }) => {
const { events } = this.state;
const nextEvents = events.map(existingEvent => {
return existingEvent.id === event.id
? { ...existingEvent, start, end }
: existingEvent;
});
this.setState({
events: nextEvents,
});
// console.log(`${event.title} was resized to ${start}-${end}`);
}
newEvent(params) {
const { currentUser, addGoogleCalendarEvent } = this.props;
const { event, formValues } = params;
const newEvent = {
title: formValues.title,
description: formValues.description ? formValues.description : null,
allDay: event.slots.length === 1,
start: moment(event.start).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
end: moment(event.end).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
};
this.setState({
calendarEvent: null,
}, () => {
addGoogleCalendarEvent({ ...newEvent, userId: currentUser.id, timezone: currentUser.timezone });
});
}
showEventModal(event) {
this.setState({ calendarEvent: event, show: true });
}
showEditEventModal(event) {
const { calendarEvents } = this.props;
const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
const item = calendarEvents.details.items[eventIndex];
this.setState({ calendarEvent: item, editShow: true });
}
hideEventModal() {
const { resetForm } = this.props;
this.setState({ show: false, calendarEvent: null }, () => {
resetForm();
});
}
hideEditEventModal() {
const { resetEditForm } = this.props;
this.setState({ editShow: false, calendarEvent: null }, () => {
resetEditForm();
});
}
render() {
const { currentGoogleUser, addEventProcess, editEventProcess } = this.props;
let authorized = false;
if (currentGoogleUser && currentGoogleUser.expires_at) {
authorized = moment(currentGoogleUser.expires_at).isAfter(moment());
}
return (
<div>
<div className="container-fluid">
<Helmet title={translate('portals.page.calendarPage.helmetTitle')} />
<section className="calendar-section">
<h2 className="main-heading">{translate('portals.page.calendarPage.pageTitle')}</h2>
{!authorized &&
<GoogleLogin
clientId={GOOGLE_CLIENT_ID}
scope="https://www.googleapis.com/auth/calendar"
className="google-login"
onSuccess={this.onSuccess}
onFailure={this.onFailure}
>
<i className="google-image" />
<span> Sign in with Google</span>
</GoogleLogin>
}
{authorized &&
<DragAndDropCalendar
selectable
events={this.state.events}
localizer={localizer}
onEventDrop={this.moveEvent}
resizable
onEventResize={this.resizeEvent}
onSelectSlot={this.showEventModal}
onSelectEvent={this.showEditEventModal}
defaultView={BigCalendar.Views.MONTH}
defaultDate={new Date()}
views={{ month: true }}
/>
}
<AddCalendarEventForm
show={this.state.show}
isSubmitting={addEventProcess.isSubmitting}
calendarEvent={this.state.calendarEvent}
onSubmit={this.newEvent}
onHide={this.hideEventModal}
/>
<EditCalendarEventForm
show={this.state.editShow}
isSubmitting={editEventProcess.isSubmitting}
calendarEvent={this.state.calendarEvent}
onSubmit={this.editEvent}
onHide={this.hideEditEventModal}
/>
</section>
</div>
</div>
);
}
}
const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
Apparently, there is an array which is called _, I can't see it anywhere in your code, you should change the way your are doing the findIndex() method.
suchlike calendarEvents.details.items.findIndex({id: event.id})
although, I don't think there is a way to find a prop value inside an object this way.
you may want to loop on instead.

Decrement and increment quantity with Reactjs

I am newbie to Reactjs and I am making a function to increase and decrease quantity. My code below:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
quantity: 1,
show: true,
max:5,
min:0
};
}
IncrementItem = () => {
if(this.state.quantity > 9) {
}else {
this.setState({
quantity: this.state.quantity + 1
});
}
}
DecreaseItem = () => {
if(this.state.quantity <= 1) {
}else {
this.setState({ quantiy: this.state.quantity - 1 });
}
}
ToggleClick = () => {
this.setState({ show: !this.state.show });
}
render() {
return (
<div>
<button onClick={this.IncrementItem}>+</button>
<input className="inputne" value={this.state.quantity} />
<button onClick={this.DecreaseItem}>-</button>
</div>
);
}
I have problems are:
Browser appears an error :
Warning: Failed prop type: You provided a value prop to a form field without an onChange handler
I cannot change value in input from View.
Please let me know how to solve. Thank for your help.
There are two issues in this code:
There is no onChange handler. Read more on how to handle onChange listeners here
Using value from this.state.quantity to increment and decrement is bad practice because setState function is asynchronous.
You can use functional version of setState to get around this:
this.setState(prevState => {
if(prevState.quantity > 0) {
return {
quantity: prevState.quantity - 1
}
} else {
return null;
}
});
Code Snippet:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
quantity: 1,
show: true,
max: 5,
min: 0
};
}
IncrementItem = () => {
this.setState(prevState => {
if(prevState.quantity < 9) {
return {
quantity: prevState.quantity + 1
}
} else {
return null;
}
});
}
DecreaseItem = () => {
this.setState(prevState => {
if(prevState.quantity > 0) {
return {
quantity: prevState.quantity - 1
}
} else {
return null;
}
});
}
ToggleClick = () => {
this.setState({
show: !this.state.show
});
}
handleChange = (event) => {
this.setState({quantity: event.target.value});
}
render() {
return ( <div>
<button onClick={this.IncrementItem}>+</button>
<input className="inputne" value={this.state.quantity} onChange={this.handleChange}/>
<button onClick = {this.DecreaseItem}>-< /button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You need to Update the value, add a onChange on Input field to do that.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
quantity: 1,
show: true,
max:5,
min:0
};
}
IncrementItem = () => {
if(this.state.quantity > 9) {
}else {
this.setState({
quantity: this.state.quantity + 1
});
}
}
DecreaseItem = () => {
if(this.state.quantity <= 1) {
}else {
this.setState({ clicks: this.state.quantity - 1 });
}
}
ToggleClick = () => {
this.setState({ show: !this.state.show });
}
UpdateValue = (e) => {
this.setState({ quantity: e.target.value });
}
render() {
return (
<div>
<button onClick={this.IncrementItem}>+</button>
<input className="inputne" value={this.state.quantity} onChange={this.UpdateValue} />
<button onClick={this.DecreaseItem}>-</button>
</div>
);
}
Try this as well it may help you
class Counters extends Component {
state = {
counters: [
{ id: 1, value: 4, max: 15, min: 0, show: true },
{ id: 2, value: 0 }`enter code here`
]
};
handleIncrement = counter => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
if (counters[index].value < counters[index].max) {
counters[index].value++;
}
this.setState({ counters });
};
handleDecrement = counter => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
if (counters[index].value > counters[index].min) {
counters[index].value--;
}
this.setState({ counters });
};
handleDelete = counterId => {
const counters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters });
};
render() {
return (
<div>
{this.state.counters.map(counter => (
<Counter
key={counter.id}
onDelete={this.handleDelete}
onIncrement={this.handleIncrement}
onDecrement={this.handleDecrement}
counter={counter}
/>
))}
</div>
);
}
}
export default Counters;
enter code here

Cannot Find what is causing this : Warning: setState(...): Can only update a mounted or mounting component

So after looking over many different questions asking the about this warning, I have found that there is not one single reason why this would be occurring making it difficult to infer a solution on my own code.
So here is my code incase anyone has another pair of eyes they can put on it and spot maybe why i would be getting this error.
This error occurs when not on first arrival of the component but after leaving and returning to it again.
I have a smart container and a dumb component set up so here is the container:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { listOrders, listUseCases } from '../../actions/order';
import { storeWithExpiration } from '../../utils/common.js';
import OrdersIndex from './OrdersIndex';
export class OrdersIndexContainer extends React.Component {
static propTypes = {
account: PropTypes.object.isRequired,
orders: PropTypes.object.isRequired,
platformMap: PropTypes.object.isRequired,
progress: PropTypes.number,
listOrdersAction: PropTypes.func.isRequired,
listUseCasesAction: PropTypes.func.isRequired,
};
render() {
const { orders, platformMap, progress } = this.props;
return (
<div>
<OrdersIndex
orders={ orders }
platformMap={ platformMap }
progress={ progress }
/>
</div>
);
}
renderOrderIndex = () => {
}
componentWillMount = () => {
const { account, listOrdersAction, listUseCasesAction } = this.props;
const token = storeWithExpiration.get('token');
listOrdersAction(token);
listUseCasesAction(account.id, token);
}
}
function mapStateToProps(state) {
const { account, orderList, progress } = state;
const orders = orderList.get('orders');
const platformMap = orderList.get('platformMap');
return { account, platformMap, orders, progress };
}
export default connect(mapStateToProps, {
listOrdersAction: listOrders,
listUseCasesAction: listUseCases,
})(OrdersIndexContainer);
And here is the dumb component:
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import ReactDataGrid from 'react-data-grid';
import { Toolbar } from 'react-data-grid/addons';
import { Data } from 'react-data-grid/addons';
import moment from 'moment';
import { SIMPLE_DATE_FORMAT } from '../../config/app_config';
// import OrderWrapFormatter from './OrderWrapFormatter';
const TABLE_COLUMNS = [
{ key: 'cNumber', name: 'Customer #', width: 125 },
{ key: 'name', name: 'Name', width: 150 },
{ key: 'orderNumber', name: 'Order #', width: 90 },
{ key: 'platform', name: 'Platform' },
{ key: 'useCase', name: 'Use Case'/* , formatter: OrderWrapFormatter */ },
{ key: 'list', name: 'List' },
{ key: 'sku', name: 'SKU' },
{ key: 'startDate', name: 'Start Date' },
{ key: 'endDate', name: 'End Date' },
];
export default class OrdersIndex extends React.Component {
static propTypes = {
orders: PropTypes.object.isRequired,
platformMap: PropTypes.object.isRequired,
progress: PropTypes.number,
};
state = {
rows: [],
originalRows: [],
columns: TABLE_COLUMNS,
sortColumn: null,
sortDirection: null,
filters: {},
}
renderRows = (orders) => {
const _rows = [];
orders.map((o) => {
_rows.push({
key: o.order.id,
id: o.order.id,
cNumber: o.order.providerCustomerNumber,
name: o.order.company.name,
orderNumber: o.order.providerOrderNumber,
platform: o.platformUseCases[0].platform.description,
useCase: this.renderMulti(o.platformUseCases, 'useCase', 'description'),
list: this.renderMulti(o.listSKUs, 'dataSet', 'name'),
sku: this.renderMulti(o.listSKUs, 'fieldSet', 'name'),
startDate: moment(o.order.startDate).format(SIMPLE_DATE_FORMAT),
endDate: moment(o.order.endDate).format(SIMPLE_DATE_FORMAT),
});
return _rows;
});
return this.setState({
rows: _rows,
originalRows: _rows.slice(),
});
}
getRows = () => {
return Data.Selectors.getRows(this.state);
}
rowGetter = (rowIdx) => {
const rows = this.getRows();
return rows[rowIdx];
}
getSize = () => {
return this.getRows().length;
}
renderMulti = (multi, itemName, subItemName) => {
const objectArray = multi.map((object) => {
return object[itemName][subItemName];
});
return objectArray.join('\n');
}
handleGridSort = (sortColumn, sortDirection) => {
const { originalRows, rows } = this.state;
const comparer = (a, b) => {
if (sortDirection === 'ASC') {
return (a[sortColumn] > b[sortColumn]) ? 1 : -1;
}
else if (sortDirection === 'DESC') {
return (a[sortColumn] < b[sortColumn]) ? 1 : -1;
}
};
const newRows = sortDirection === 'NONE' ? originalRows.slice() : rows.sort(comparer);
this.setState({
rows: newRows,
});
}
handleRowUpdated = (e) => {
// merge updated row with current row and rerender by setting state
const { rows } = this.state;
Object.assign(rows[e.rowIdx], e.updated);
this.setState({
...rows,
});
}
handleFilterChange = (filter) => {
const { filters } = this.state;
const newFilters = Object.assign({}, filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
}
else {
delete newFilters[filter.column.key];
}
this.setState({
filters: newFilters,
});
}
onClearFilters = () => {
// all filters removed
this.setState({
filters: {},
});
}
// Creates appropriate warnings to prevent entering
// the order form if the account is missing information
renderNotice = (message, buttonMessage, route) => {
return (
<div className="alert alert-warning">
<strong>Notice:</strong>
<p>{ message }</p>
<p>
<Link
to={ route }
className="btn btn-warning"
>
<i className='fa fa-plus'></i>
{ buttonMessage }
</Link>
</p>
</div>
);
}
render() {
const { platformMap, progress } = this.props;
const platformMessage = 'Your account is not associated with any platform use cases.' +
'You must select at least one use case before creating new orders.';
const platformButton = 'Add Use Cases';
const platformRoute = '/products';
return (
<div className="container">
<div className="row">
<div className="col-sm-12 col-md-8">
<h1>Orders</h1>
</div>
<div className="col-sm-12 col-md-4">
<span className="pull-right">
<Link
to="/orders/create/1"
className="btn btn-primary"
disabled
>
<i className='fa fa-plus'></i>Create New Order
</Link>
</span>
</div>
</div>
{ platformMap.size === 0 && progress === 0 ?
this.renderNotice(platformMessage, platformButton, platformRoute) : null }
<div className="row">
{ progress === 0 ?
<div className="col-md-12">
{ this.renderTable() }
</div> : null }
</div>
</div>
);
}
renderTable = () => {
const { orders } = this.props;
const { columns } = this.state;
return (
<div>
{ orders.size === 0 || orders === undefined ?
<p>Your account has no orders</p> :
<ReactDataGrid
onGridSort={ this.handleGridSort }
rowKey="key"
id="key"
columns={ columns }
rowGetter={ this.rowGetter }
rowsCount={ this.getSize() }
onRowUpdated={ this.handleRowUpdated }
toolbar={ <Toolbar enableFilter /> }
onAddFilter={ this.handleFilterChange }
onClearFilters={ this.onClearFilters }
minHeight={ 500 }
filterRowsButtonText="Search By Field"
/>
}
</div>
);
}
componentWillMount = () => {
const { orders } = this.props;
const columnArray =
TABLE_COLUMNS.map((c) => {
const copy = Object.assign({}, c);
copy.filterable = true;
copy.locked = true;
if (copy.key !== 'useCase') {
copy.sortable = true;
}
return copy;
});
this.setState({
columns: columnArray,
});
this.renderRows(orders);
}
componentWillReceiveProps = (nextProps) => {
const { orders } = nextProps;
if (orders.size > 0) {
this.renderRows(orders);
}
}
}
I understand this might be a lot but I cannot for the life of me determine what could be the cause. Thanks to anyone who takes a look.

Resources