Component wont render from mapStateToProps - reactjs

I am using the events calendar package, https://www.npmjs.com/package/react-native-events-calendar, I am trying to display the EventsCalendar component with events coming from redux.
events={fetchEvents[selected].dots} is an array but the component will not render, however if I copy the array directly into the component it renders.
Any ideas why my component won't render?
import React, { Component } from "react";
import { connect } from "react-redux";
import {
Text,
View,
StyleSheet,
Image,
Dimensions,
ActivityIndicator
} from "react-native";
import Icon from "react-native-vector-icons/FontAwesome";
import * as helpers from "../../helpers";
import {
fetchEvents,
changeMonth,
eventsForAgendaMonth
} from "../../actions/events";
import CustomHeader from "../../navigation/CustomHeader";
import XDate from "xdate";
import AgendaItem from "./AgendaItem";
import AgendaDay from "./AgendaDay";
import EventCalendar from "./DayView";
import propTypes from "prop-types";
class AgendaScreen extends Component {
static navigationOptions = {
title: "",
header: <CustomHeader />
};
constructor(props) {
super(props);
this._eventTapped = this._eventTapped.bind(this);
this.dateChanged = this.dateChanged.bind(this);
this.renderEvent = this.renderEvent.bind(this);
}
dateChanged(date) {
const { eventsForYear, eventsMonth } = this.props;
this.setState({ selectedDay: date });
this.props.dispatch(eventsForAgendaMonth(eventsForYear, date));
}
_eventTapped(event) {
alert(JSON.stringify(event));
}
loadItems(day) {
const { eventsForYear } = this.props;
this.props.dispatch(eventsForAgendaMonth(eventsForYear, day));
}
renderEvent(event) {
console.log("event", event);
return <AgendaItem {...event} />;
}
render() {
const {
fetchEvents,
navigation: {
state: {
params: {
day: { dateString: selected }
}
}
}
} = this.props;
const currentDate = helpers.currentDate;
let { width } = Dimensions.get("window");
return (
<View style={{ flex: 1 }}>
<EventCalendar
eventTapped={this._eventTapped.bind(this)}
events={fetchEvents[selected].dots}
width={width}
initDate={selected}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
fetchEvents: state.fetchEvents
};
};
export default connect(mapStateToProps)(AgendaScreen);
AgendaScreen.propTypes = {
eventsMonth: propTypes.object,
eventsForYear: propTypes.object
};
const styles = {
container: {
backgroundColor: "#00000000"
},
event: {
backgroundColor: "#00000000",
borderColor: "#DDE5FD",
borderWidth: 0,
borderRadius: 0,
paddingTop: 3,
paddingBottom: 2
},
header: {
height: 30,
paddingHorizontal: 30
},
headerText: {}
};
AFAIK I am not mutating state, I am createing a new object with my reducer:
case "EVENTS_SUCCESS":
const events = {};
const filteredEvents = action.data
.filter(event => {
if (
event.status === "cancelled" ||
!event.start.dateTime ||
helpers.checkDate(event.start.dateTime)
) {
return false;
}
return true;
})
.sort((a, b) => a.startTimeString - b.startTimeString);
filteredEvents.map(event => {
const {
start: { dateTime: startTime },
end: { dateTime: endTime },
summary,
id
} = event;
const sDate = new Date(startTime);
const eDate = new Date(endTime);
const startHour = sDate.getHours();
const startMinute = sDate.getMinutes();
const startDate =
sDate.getFullYear() +
"-" +
("0" + (sDate.getMonth() + 1)).slice(-2) +
"-" +
("0" + sDate.getDate()).slice(-2);
const endHour = eDate.getHours();
const endMinute = eDate.getMinutes();
const endDay = eDate.getDay();
const endMonth = eDate.getMonth();
const endYear = eDate.getFullYear();
const eventStartTime = `${startHour}-${startMinute}`;
const eventEndTime = `${endHour}-${endMinute}`;
let newEvent = {
title: summary,
summary,
id,
eventStartTime,
eventEndTime
};
events[startDate] = events[startDate] || { dots: [] };
const dotsEvents = [...events[startDate].dots, newEvent];
dotsEvents.sort(
(a, b) => a.startTimeString - b.startTimeString
);
events[startDate] = {
dots: dotsEvents,
disabled: false,
selected: true,
selectedColor: "#00CCCB",
customStyles: {
text: {
marginTop: 3
}
}
};
});
return {
...events
};

Related

How to scroll up history in React chat page

*Trying to show a chat history with infinite reload similar to Skype or any popular chat app
In a chat page. If my chat messages limit is 10 messages.
And the chat has 30.
It will show latest 10 when I load the chat.
When I scroll to the top I want to see the previous 10.
I tried this myself and the scroll position stays the same but the messages load in the view.
It should load to the top and hold the scroll position.
How can this be done?
Here's my page:
https://pastebin.com/36xZPG1W
import React, { useRef, useState, useEffect } from 'react';
import produce from 'immer';
import dayjs from 'dayjs';
import { WithT } from 'i18next';
import * as ErrorHandler from 'components/ErrorHandler';
import useOnScreen from 'utils/useOnScreen';
import getLang from 'utils/getLang';
import Message from './Message';
const limit = 10;
const lang = getLang();
interface IMessagesProps extends WithT {
messages: any;
currentUserID: string;
chatID: string;
fetchMore: any;
typingText: any;
setSelectedMsg: any;
removeMessage: any;
}
const Messages: React.FC<IMessagesProps> = ({
messages,
currentUserID,
chatID,
fetchMore,
setSelectedMsg,
removeMessage,
t,
}) => {
const elementRef = useRef(null);
const isOnScreen = useOnScreen(elementRef);
const topElementRef = useRef(null);
const topIsOnScreen = useOnScreen(topElementRef);
const isUserInside = useRef(true);
const scroller = useRef<HTMLDivElement>(null);
const messagesEnd = useRef<HTMLDivElement>(null);
const [hasMore, setHasMore] = useState(true);
useEffect(() => {
scrollToBottom();
}, []);
useEffect(() => {
autoscroll();
}, [messages]);
//NOT WORKING
const autoscroll = () => {
// Visible height
const visibleHeight = scroller.current.offsetHeight;
// Height of messages container
const containerHeight = scroller.current.scrollHeight;
// How far have I scrolled?
const scrollOffset = scroller.current.scrollTop + visibleHeight;
// New message element
const firstChild = scroller.current.firstElementChild;
console.log(`visibleHeight`, visibleHeight);
console.log(`containerHeight`, containerHeight);
console.log(`scrollOffset`, scrollOffset);
console.log(`firstChild`, firstChild.offsetHeight);
console.log(`firstChild`, firstChild.scrollHeight);
console.log(`firstChild`, firstChild);
scroller.current.scrollTop = scrollOffset;
// // Height of the new message
// const newMessageStyles = getComputedStyle($newMessage)
// const newMessageMargin = parseInt(newMessageStyles.marginBottom)
// const newMessageHeight = $newMessage.offsetHeight + newMessageMargin
// // Visible height
// const visibleHeight = $messages.offsetHeight
// // Height of messages container
// const containerHeight = $messages.scrollHeight
// // How far have I scrolled?
// const scrollOffset = $messages.scrollTop + visibleHeight
// if (containerHeight - newMessageHeight <= scrollOffset) {
// $messages.scrollTop = $messages.scrollHeight
// }
};
const fetchDataForScrollUp = cursor => {
ErrorHandler.setBreadcrumb('fetch more messages');
if (!hasMore) {
return;
}
fetchMore({
variables: {
chatID,
limit,
cursor,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult?.getMessages || fetchMoreResult.getMessages.messages.length < limit) {
setHasMore(false);
return previousResult;
}
const newData = produce(previousResult, draftState => {
draftState.getMessages.messages = [...previousResult.getMessages.messages, ...fetchMoreResult.getMessages.messages];
});
return newData;
},
});
};
if (messages?.length >= limit) {
if (topIsOnScreen) {
fetchDataForScrollUp(messages[messages.length - 1].id);
}
}
if (isOnScreen) {
isUserInside.current = true;
} else {
isUserInside.current = false;
}
const scrollToBottom = () => {
if (messagesEnd.current) {
messagesEnd.current.scrollIntoView({ behavior: 'smooth' });
}
};
const groupBy = function (arr, criteria) {
return arr.reduce(function (obj, item) {
// Check if the criteria is a function to run on the item or a property of it
const key = typeof criteria === 'function' ? criteria(item) : item[criteria];
// If the key doesn't exist yet, create it
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
obj[key] = [];
}
// Push the value to the object
obj[key].push(item);
// Return the object to the next item in the loop
return obj;
}, {});
};
const objectMap = object => {
return Object.keys(object).reduce(function (result, key) {
result.push({ date: key, messages: object[key] });
return result;
}, []);
};
const group = groupBy(messages, datum => dayjs(datum.createdAt).locale(lang).format('dddd, MMMM D, YYYY').toLocaleUpperCase());
const messageElements = objectMap(group)
.reverse()
.map((item, index) => {
const messageElements = item.messages
.map(message => {
return (
<Message
key={uniqueKey}
message={message}
currentUserID={currentUserID}
lang={lang}
removeMessage={removeMessage}
t={t}
chatID={chatID}
setSelectedMsg={setSelectedMsg}
/>
);
})
.reverse();
return messageElements;
})
.reduce((a, b) => a.concat(b), []);
return (
<div style={{ marginBottom: '25px' }}>
<div ref={topElementRef} />
<div
style={{
position: 'relative',
display: 'flex',
flexDirection: 'column',
flexWrap: 'wrap',
height: '100%',
overflow: 'hidden',
}}
ref={scroller}
>
{messageElements}
<div ref={elementRef} style={{ position: 'absolute', bottom: '5%' }} />
</div>
</div>
);
};
export default Messages;
Been stuck on this for 2 weeks lol. Any advice is helpful :)
Have you tried scrollIntoView ? you can try after changing your autoscroll function like following
const autoscroll = () => {
elementRef.current.scrollIntoView({ behavior: 'smooth' })
};

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..!!

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.

monitor.getDropResult() return null

monitor.getDropResult() returns null (I look it console.log). It should return object(dragged item) with its position. Why does it return null?
I signature my component with DragSource,DropTarget..but it still returns null
Here is my entire component code:
import React, { PropTypes } from 'react';
import { DragSource } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { StoneConstants, StoneTypes, ItemTypes } from 'constants/AppConstants';
import OkeyStoneBase from 'components/OkeyStoneBase/OkeyStoneBase';
import './_OkeyStone.scss';
function checkForDragAction(props) {
// TODO receive the action via prop
if (props.stoneType === StoneTypes.ON_WITHDRAW_MIDDLE) {
props.onWithdrawMiddle();
} else if (props.stoneType === StoneTypes.ON_DISCARD_WEST) {
props.onWithdrawLeft();
}
}
function applyDropResult(props, result) {
if (props.stoneType === StoneTypes.ON_WITHDRAW_MIDDLE || props.stoneType === StoneTypes.ON_DISCARD_WEST) {
if (result === null) { //taşı tahtaya koymadıysa
}
props.withdrawRequestAtPosition(result.top, result.left);
}
if (props.stoneType === StoneTypes.ON_RAKE) {
if (result && result.stackType === StoneTypes.ON_DISCARD_SOUTH) {
props.onDiscardStone({
id: props.id,
number: props.number,
color: props.color
});
}
}
}
const stoneSource = {
canDrag(props) {
return props.draggable;
},
beginDrag(props) {
if (props.onBeginDrag) {
props.onBeginDrag();
}
checkForDragAction(props);
return {
id: props.id,
stoneType: props.stoneType,
left: props.left,
top: props.top,
color: props.color,
number: props.number
};
},
endDrag(props, monitor) {
if (props.onEndDrag) {
props.onEndDrag();
}
console.log(props.onEndDrag);
console.log(monitor);
***var result = monitor.getDropResult();***
console.log('stoneSource'+result);
applyDropResult(props, result);
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
};
}
function getStyles(props) {
const scale = StoneConstants.MINI_SCALE;
let { left, top, isDragging, isMini } = props;
const { zIndex } = props;
const { canTransition } = props;
let transform = `translate3d(${left}px, ${top}px, 0)`;
if (isMini) {
transform += ` scale(${scale}, ${scale})`;
}
let result = {
transformOrigin: 'top left',
transform: transform,
WebkitTransform: transform,
zIndex: zIndex,
opacity: isDragging ? 0 : 1,
height: isDragging ? 0 : ''
};
if (isDragging || !canTransition) {
result.transition = 'none';
}
return result;
}
class OkeyStone extends React.Component {
handleStoneClick (e) {
const { id, onClicked } = this.props;
if (onClicked) {
e.stopPropagation();
onClicked(id);
}
}
componentDidMount() {
this.props.connectDragPreview(getEmptyImage(), {
captureDraggingState: true
});
}
render() {
let { connectDragSource } = this.props;
let { number, color } = this.props;
let { isStable, isSelected } = this.props;
let stableStyle = isStable ? 'okey-stone-stable' : '';
return connectDragSource(
<div className={'okey-stone-parent ' + stableStyle}
onClick={this.handleStoneClick}
style={getStyles(this.props)}>
<OkeyStoneBase number={number} color={color} isSelected={isSelected}/>
</div>
);
}
}
OkeyStone.propTypes = {
connectDragSource: PropTypes.func,
connectDragPreview: PropTypes.func,
isDragging: PropTypes.bool,
id: PropTypes.number,
left: PropTypes.number,
top: PropTypes.number,
stoneType: PropTypes.string,
isStable: PropTypes.bool,
isMini: PropTypes.bool
};
export default DragSource(ItemTypes.STONE, stoneSource, collect)(OkeyStone);
You need to create a DropTarget and define a drop() function within its source, and whatever that returns will be what is returned by the monitor.getDropResult() function inside of the DragSource's endDrag() function (per http://gaearon.github.io/react-dnd/docs-drag-source-monitor.html). I'm not sure what you'd like the component itself to look like, but if you created a DropTarget with a drop function resembling:
const stoneDropSource = {
drop(props, monitor) {
return monitor.getItem();
},
}
Then that is what you would receive from calling getDropResult() in the endDrag() function.

react-dnd uncaught typerrors. Trying to follow the simple sortable example

I've been trying to work off of the simple sortable example in the react-dnd examples but I am having trouble trying to convert the es7 code to es6. I've tried using babel but I don't really understand the code that it spits out.
Here is my code that I've tried to translate from es7 to es6:
import React, {PropTypes} from 'react';
import Router from 'react-router';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragSource, DropTarget } from 'react-dnd';
const style= {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move'
}
const ItemTypes = {
Coursepage: 'coursepage'
};
const coursePageSource = {
beginDrag(props) {
return {
id: props.id,
index: props.index
}
}
}
const coursePageTarget = {
hover(props, monitor, component){
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
//don't replace items with themselves
if(dragIndex === hoverIndex){
return;
}
//Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
//get vertical middle
const hoverMiddleY = (hoverBoundingRect.Bottom - hoverBoundingRect.Top) /2;
//get top pixels
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
//only perform the move when the mouse has crossed half of the items height
//when dragging downwards, only move when the cursor is below 50%
//when dragging upwards, only move when the cursor is above 50%
//dragging downwards
if(dragIndex < hoverIndex && hoverClientY < hoverMiddleY){
return;
}
//dragging upwards
if(dragIndex > hoverIndex && hoverClientY > hoverMiddleY){
return;
}
//time to actually perform the action
props.moveObject(dragIndex, hoverIndex);
}
}
// const propTypes = {
// connectDragSource: PropTypes.func.isRequired,
// connectDropTarget: PropTypes.func.isRequired,
// index: PropTypes.number.isRequired,
// isDragging: PropTypes.bool.isRequired,
// id: PropTypes.any.isRequired,
// text: PropTypes.string.isRequired,
// moveCard: PropTypes.func.isRequired
// };
function collectDropTarget(connect) {
return {
connectDropTarget: connect.dropTarget(),
};
}
/**
* Specifies which props to inject into your component.
*/
function collectDragSource(connect, monitor) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource(),
// You can ask the monitor about the current drag state:
isDragging: monitor.isDragging()
};
}
class Coursepage extends React.Component{
render(){
console.log(this.props);
const {text, isDragging, connectDragSource, connectDropTarget} = this.props;
const opacity = isDragging ? 0 : 1;
return connectDragSource(connectDropTarget(
<div style={{opacity}}>
{text}
</div>
));
}
}
// Coursepage.propTypes = propTypes;
export default DragSource(ItemTypes.Coursepage, coursePageSource, collectDragSource)(Coursepage);
export default DropTarget(ItemTypes.Coursepage, coursePageTarget, collectDropTarget)(Coursepage);
Now the error I'm getting from this is
"Uncaught TypeError: connectDropTarget is not a function."
I console logged this.props in render and I see that connectDragSource is showing up in the this.props object but not connectDropTarget.
Can anyone tell me what I'm missing?
By the way, this is the example code I was using:
https://github.com/gaearon/react-dnd/blob/master/examples/04%20Sortable/Simple/Card.js
I know this is a little old but I landed up here through google so I figured I would give it a go. First of all, you can't have two default exports as referenced here in section 3.2 http://www.2ality.com/2014/09/es6-modules-final.html
Instead you need to pass the result of one of your current default exports into the second function call - you'll see below.
This took me a couple of hours to get working as I'm also an Es6/7 newbie - so I invite any criticism!
// Container.js;
import React, { Component } from 'react';
import update from 'react/lib/update';
import Card from './Card';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
const style = {
width: 400
};
class Container extends Component {
constructor(props) {
super(props);
this.moveCard = this.moveCard.bind(this);
this.findCard = this.findCard.bind(this);
this.state = {
cards: [{
id: 1,
text: 'Write a cool JS library'
}, {
id: 2,
text: 'Make it generic enough'
}, {
id: 3,
text: 'Write README'
}, {
id: 4,
text: 'Create some examples'
}, {
id: 5,
text: 'Spam in Twitter and IRC to promote it (note that this element is taller than the others)'
}, {
id: 6,
text: '???'
}, {
id: 7,
text: 'PROFIT'
}]
};
}
findCard(id) {
const { cards } = this.state;
const card = cards.filter(c => c.id === id)[0];
return {
card,
index: cards.indexOf(card)
};
}
moveCard(id, atIndex) {
const { card, index } = this.findCard(id);
this.setState(update(this.state, {
cards: {
$splice: [
[index, 1],
[atIndex, 0, card]
]
}
}));
}
render() {
const { cards } = this.state;
return (
<div style={style}>
{cards.map((card, i) => {
return (
<Card key={card.id}
index={i}
id={card.id}
text={card.text}
moveCard={this.moveCard}
findCard={this.findCard} />
);
})}
</div>
);
}
}
export default DragDropContext(HTML5Backend)(Container)
Then Card.js
// Card.js
import React, { Component, PropTypes } from 'react';
import ItemTypes from './ItemTypes';
import { DragSource, DropTarget } from 'react-dnd';
const style = {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move'
};
const cardSource = {
beginDrag(props) {
return {
id: props.id,
originalIndex: props.findCard(props.id).index
};
},
endDrag(props, monitor) {
const { id: droppedId, originalIndex } = monitor.getItem();
const didDrop = monitor.didDrop();
if (!didDrop) {
props.moveCard(droppedId, originalIndex);
}
}
};
const cardTarget = {
canDrop() {
return false;
},
hover(props, monitor) {
const { id: draggedId } = monitor.getItem();
const { id: overId } = props;
if (draggedId !== overId) {
const { index: overIndex } = props.findCard(overId);
props.moveCard(draggedId, overIndex);
}
}
};
function collect(connect, monitor) {
console.log( "HERE2", connect );
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
};
}
function collect2(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
};
}
class Card extends Component {
render() {
const { text, isDragging, connectDragSource, connectDropTarget } = this.props;
const opacity = isDragging ? 0 : 1;
return connectDragSource(connectDropTarget(
<div >
{text}
</div>
));
}
}
Card.propTypes = {
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
id: PropTypes.any.isRequired,
text: PropTypes.string.isRequired,
moveCard: PropTypes.func.isRequired,
findCard: PropTypes.func.isRequired
};
const x = DropTarget(ItemTypes.CARD, cardTarget, collect )(Card)
export default DragSource(ItemTypes.CARD, cardSource, collect2 )( x )
And then the types include
// ItemTypes.js
export default {
CARD: 'card'
};

Resources