Array.map is not rendering in Reat Native Function Component - javascript

I'm trying to render components with a loop. What I did was to use array.map inside a JSX return block of the function component. The component is to render each individual dates contained in the list called 'mountDates'. But it is not rendering the items. Here is my code:
import React, {useEffect} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {globalStyles} from '../../styles/global';
import AgendaItem from './agendaItem';
export default function Agenda({onDayChange, startDate, endDate}) {
const mountDates = [];
useEffect(() => {
const getDates = (startDate, endDate) => {
let currentDate = startDate;
console.log(startDate, endDate);
while (currentDate.getTime() <= endDate.getTime()) {
mountDates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
}
};
getDates(startDate, endDate);
}, []);
return (
<View style={styles.container}>
{mountDates.map((date, i) => {
return <AgendaItem key={i.toString()} date={date} />;
})}
</View>
);
}

Simply use React.useState. which will trigger re-render when all data is set.
import {View, Text, StyleSheet} from 'react-native';
import {globalStyles} from '../../styles/global';
import AgendaItem from './agendaItem';
export default function Agenda({onDayChange, startDate, endDate}) {
const [mountDates, setMountDates] = React.useState([]);
useEffect(() => {
const tempMountDates = [];
const getDates = (startDate, endDate) => {
let currentDate = startDate;
console.log(startDate, endDate);
while (currentDate.getTime() <= endDate.getTime()) {
tempMountDates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
}
};
getDates(startDate, endDate);
setMountDates();
}, []);
return (
<View style={styles.container}>
{mountDates.map((date, i) => {
return <AgendaItem key={i.toString()} date={date} />;
})}
</View>
);
}

Related

My react-native-app freezes when i use while loop

This component displays calendar to the patients so that they can select the appointment day from the appointment days of doctor. Doctor appointment days are fetched from api. What i am trying to achieve is to disable all other weekdays days in the calendar except the doctor appointment days so that patients can only press one of the appointment days. i am using react-native-calendars library and date-fns-library for dates. However my app is freezing once while loop is being defined. What am i doing wrong here ? Also is there a better way of doing what i am trying to achieve?
import { View } from "react-native";
import React, { useEffect, useState } from "react";
import { Calendar, CalendarProps } from "react-native-calendars";
import startOfMonth from "date-fns/startOfMonth";
import endOfMonth from "date-fns/endOfMonth";
import isBefore from "date-fns/isBefore";
import addDays from "date-fns/addDays";
import format from "date-fns/format";
import setDay from "date-fns/setDay";
import api from "../../config/api";
import Layout from "../UI/Layout";
import RegularText from "../UI/Text/RegularText";
import { useAppSelector } from "../../store/hooks";
import { useRoute } from "#react-navigation/native";
import { AppointmentDaysScreenRouteProp } from "../../#types/navigation";
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;
type weekday = typeof weekdays[number];
const CalendarComponent = () => {
const language = useAppSelector((state) => state.language.selected);
const [markedDates, setMarkedDates] = useState<CalendarProps["markedDates"]>(
{}
);
const [disabledledWeekdays, setDisbaledWeekdays] = useState<number[]>([]);
const route = useRoute<AppointmentDaysScreenRouteProp>();
const { doctorId } = route.params;
const [loading, setLoading] = useState(true);
let text = {
loading: "...Please wait",
};
if (language === "اردو") {
text = {
loading: "...لوڈ ہو رہا ہے",
};
}
useEffect(() => {
(async () => {
try {
const res = await api.get<{ appointmentDays: weekday[] }>(
`/appointments/appointmentDays/doctorId/${doctorId}`
);
const { appointmentDays } = res.data;
const disabledDays = weekdays
.filter((item) => !appointmentDays.includes(item))
.map((item) => weekdays.indexOf(item));
const now = new Date();
getDisabledDays(now.getMonth(), now.getFullYear(), disabledDays);
setDisbaledWeekdays(disabledDays);
} finally {
setLoading(false);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const getDisabledDays = (
month: number,
year: number,
daysIndexes: number[]
) => {
const now = new Date();
now.setFullYear(year, month);
const pivot = startOfMonth(now);
const end = endOfMonth(now);
const dates: CalendarProps["markedDates"] = {};
const disabled = { disabled: true, disableTouchEvent: true };
//THIS WHILE LOOP IS FREEZING MY APP
//IF I REMOVE THIS LOOP APP WORKS FINE
while (isBefore(pivot, end)) {
daysIndexes.forEach((day) => {
const copy = setDay(new Date(pivot), day);
dates[format(copy, "yyyy-MM-dd")] = disabled;
});
addDays(pivot, 7);
}
setMarkedDates(dates);
return dates;
};
return (
<Layout>
<View>
{loading ? (
<RegularText>{text.loading}</RegularText>
) : (
<Calendar
theme={{
textSectionTitleDisabledColor: "#d9e1e8",
}}
markedDates={markedDates}
onDayPress={(day) => {
console.log("dosomething with ", day);
}}
firstDay={1}
enableSwipeMonths={true}
disabledDaysIndexes={disabledledWeekdays}
onMonthChange={(date) => {
getDisabledDays(date.month - 1, date.year, disabledledWeekdays);
}}
/>
)}
</View>
</Layout>
);
};
export default CalendarComponent;
Looking at the documentation, I'm it seems that addDays(date, amount) is returning a new date and not modifying the value of pivot. Try doing pivot = addDays(pivot, 7)

airbnb/react-dates DayPickerRangeController set visible month on some function call

Is there way to set DayPickerRangeController custom visible month after component rendered?
I have 'handleMonthChange' function which I want to change visible month when this function is called. I try to set 'initialVisible' month but it is not working.
import { useState } from "react";
import moment from "moment";
import { DayPickerRangeController } from "react-dates";
import "react-dates/initialize";
import "react-dates/lib/css/_datepicker.css";
const Date = (props) => {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [focusedInput, setFocusedInput] = useState("startDate");
const [initialMonth, setInitialMonth] = useState(moment("01-01-2021"));
const handleDatesChange = ({ startDate, endDate }) => {
setStartDate(moment(startDate, "MMM DD, YYYY"));
setEndDate(moment(endDate, "MMM DD, YYYY"));
};
const handleMonthChange = () => {
console.log("month change");
setInitialMonth(moment("01-06-2021"));
};
return (
<div>
<DayPickerRangeController
onDatesChange={handleDatesChange}
focusedInput={focusedInput}
startDate={startDate}
endDate={endDate}
numberOfMonths={2}
initialVisibleMonth={() => initialMonth}
/>
<button onClick={handleMonthChange}>Change Visible Month</button>
</div>
);
};
export default Date;
I found workaround solution. Unmounting component and then render with new initialMonth
import { useState } from "react";
import moment from "moment";
import { DayPickerRangeController } from "react-dates";
import "react-dates/initialize";
import "react-dates/lib/css/_datepicker.css";
const Date = (props) => {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [focusedInput, setFocusedInput] = useState("startDate");
const [initialMonth, setInitialMonth] = useState(moment("01-01-2021"));
const handleDatesChange = ({ startDate, endDate }) => {
setStartDate(moment(startDate, "MMM DD, YYYY"));
setEndDate(moment(endDate, "MMM DD, YYYY"));
};
const handleMonthChange = () => {
setInitialMonth(null)
setTimeout(() => setInitialMonth(moment("01-06-2021")), 0);
};
if(!initialMonth) return <div>Loading...</div>
return (
<div>
<DayPickerRangeController
onDatesChange={handleDatesChange}
focusedInput={focusedInput}
startDate={startDate}
endDate={endDate}
numberOfMonths={2}
initialVisibleMonth={() => initialMonth}
/>
<button onClick={handleMonthChange}>Change Visible Month</button>
</div>
);
};
export default Date;

change style of calendar component in react js

I wanted to change my calendar component , from an old one to new one , they both exist on the website , but the new one isn't working, I want to make it work , when the user choose a date, it reacts with the website
this is my old one :
This is the Code :
import 'd3-transition';
import React, { Component } from 'react';
import { connect } from "react-redux";
import { setDatePrecision, nextDate, previousDate, loadWords, loadArticles } from "../redux/actions";
class DaySelector extends Component {
state = {
datePrecision: "day",
selectedDate: new Date()
};
render() {
const rthis = this.props;
const prev = () => {
rthis.previousDate();
this.props.loadWords();
this.props.loadArticles();
};
const next = () => {
rthis.nextDate();
this.props.loadWords();
this.props.loadArticles();
}
const dayPrecision = () => {
rthis.setDatePrecision("day");
this.props.loadWords();
this.props.loadArticles();
}
const monthPrecision = () => {
rthis.setDatePrecision("month");
this.props.loadWords();
this.props.loadArticles();
}
const current_date = this.props.selectedDate;
const datePrecision = this.props.datePrecision;
const year = current_date.getFullYear();
const month = current_date.getMonth() + 1;
const day = current_date.getDate();
return (
<div>
<a href="#day" onClick={dayPrecision}>day </a>
<a href="#month" onClick={monthPrecision}>month </a>
<a href="#prev" onClick={prev}><<<</a>
{datePrecision === "day" ? String(day).padStart(2, "0") + "/" : ""}{String(month).padStart(2, "0")}/{year}
<a href="#next" onClick={next}>>>></a>
</div>
);
}
}
const mapStateToProps = state => {
return {
selectedDate: state.wordsReducer.selectedDate,
datePrecision: state.wordsReducer.datePrecision,
}
};
export default connect(mapStateToProps, { setDatePrecision, nextDate, previousDate, loadWords, loadArticles })(DaySelector);
I want to replace it with this new Calendar :
this is the code of this component :
import React, { useState } from 'react';
import Calendar from 'react-calendar';
import 'react-calendar/dist/Calendar.css';
const MyCalendar = () => {
const [date, setDate] = useState(new Date());
const onChange = (date) => setDate(date);
return (
<div>
<h5 className="card-title mb-0">Calendar</h5>
<Calendar onChange={onChange} value={date} />
</div>
);
};
export default MyCalendar;
Those components are both on the website but I could not make it dynamically work,
I've install it from Here
Thank you !
Looks like you are missing to pass the prop from onChange, try:
<Calendar onChange={(value, event) => onChange(value)} value={date} />
or try:
<Calendar onChange={(value, event) => setDate(value)} value={date} />

react / classify date

I get dat from backend and I should classify these data according to creation date( today, yesterday, other).
I am using typescript react with ant design.
My code is working but I believe that there is a clean and better way to do achieve this feature
Here is my code:
import React, { useEffect, useState } from 'react';
import styles from './app.module.scss';
import { List } from 'antd';
import { getDataList } from '../../services';
import { Case } from '../../interfaces/Case';
import moment from 'moment';
import 'moment/locale/ar';
const App = () => {
const [todayData, setTodayData] = useState<Case[]>([]);
const [yesterdayData, setYesterdayData] = useState<Case[]>([]);
const [data, setData] = useState<Case[]>([]);
moment.locale('ar')
let todaysDate = new Date();
let yesterdayDate = new Date(todaysDate);
yesterdayDate.setDate(todaysDate.getDate() - 1);
const today = moment(todaysDate).locale('ar').format('dddd, DD MMM');
const yesterday = moment(yesterdayDate).locale('ar').format('dddd, DD MMM');
useEffect(() => {
getDataList()
.then((res) => {
const todayList = res.data.filter(function (data: Case) {
return moment(data.createdAt).locale('ar').format('dddd, DD MMM') === today;
});
const yesterdayList = res.data.filter(function (data: Case) {
return moment(data.createdAt).locale('ar').format('dddd, DD MMM') === yesterday;
});
const list = res.data.filter(function (data: Case) {
return data.createdAt !== today && data.createdAt !== yesterday;
});
setTodayData(todayList);
setYesterdayData(yesterdayList);
setData(list);
})
.catch((error) => {
});
}, []);
return (
<>
<List
className={styles['today_style']}
dataSource={todayData}
renderItem={(item) => (
<List.Item>
<h1> Hiiii today {item.createdAt} </h1>>
</List.Item>
)}
/>
<List
className={styles['yesterday_style']}
dataSource={yesterdayData}
renderItem={(item) => (
<List.Item>
<h1> Hiiii yesterday {item.createdAt} </h1>>
</List.Item>
)}
/>
<List
className={styles['style']}
dataSource={data}
renderItem={(item) => (
<List.Item>
<h1> Hiiii {item.createdAt} </h1>>
</List.Item>
)}
/>
</>
);
};
export default App;
any suggestions would be helpful,
thank you
I think that looks ok; I would just move those variables into the useEffect since its dependent on them. Make sure you also have react dev tools installed on chrome; that will give you any warnings.
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
Edit:
You could also combine your state into a single object with 3 array properties so that you only make one update call, but again, I don't think it's necessary.

How to edit the time picker of material-ui-time-picker?

I have a material-ui-time-picker and I want to control this input, it works well, but I want to edit the time input from the keyboard and not when I click the input on the clock.
My code is :
import React, { Component } from "react";
import { TimePicker } from "material-ui-time-picker";
import { Input as Time, Dialog as Clock } from "#material-ui/core";
openDialog = () => this.setState({ isOpen: true });
closeDialog = () => this.setState({ isOpen: false });
handleDialogTimeChange = newValue => {
const hours = newValue
.getHours()
.toString()
.padStart(2, "0");
const minutes = newValue
.getMinutes()
.toString()
.padStart(2, "0");
const textValue = hours + ":" + minutes;
this.setState({ time: textValue });
};
handleKeyboardTimeChange = time => this.setState({ time });
createDateFromTextValue = value => {
const splitParts = value.split(":");
return new Date(1970, 1, 1, splitParts[0], splitParts[1]);
};
render() {
return (
<div>
<Time
value={this.state.time}
onChange={this.handleKeyboardTimeChange}
endAdornment={
<InputAdornment position="end">
<IconButton onClick={this.openDialog}>
<AccessTime />
</IconButton>
</InputAdornment>
}
//}
/>
<Clock maxWidth="xs" open={this.state.isOpen}>
<TimePicker
mode="24h"
value={this.createDateFromTextValue(this.state.time)}
onChange={this.handleDialogTimeChange}
autoOk={true}
cancelLabel=""
okLabel=""
placeholder=""
disableUnderline={true}
/>
</Clock>
</div>
);
}
My sandbox: https://codesandbox.io/s/vm9wm19p27
When I run it, I get this input, but when I edit his value, the input will be disappeared.
How can I fix it ?
One solution component has been provided in their Github repository. Please check this out, it's an already know issue with material-ui and has already been accepted as a solution.This is the solution provided there incase the link becomes obsolete:
'use strict';
import React, {Component} from 'react';
import {DatePicker, IconButton, TextField} from "material-ui";
import ActionDateRange from 'material-ui/svg-icons/action/date-range';
import format from 'date-fns/format'
import parse from 'date-fns/parse'
export default class DatePickerField extends Component{
constructor(props){
super(props);
this.state = {
selectedDate: new Date(),
dateText: format(new Date(), 'MM/DD/YYYY')
};
}
handleChangeDatePicker = (event, date) => {
this.setState({selectedDate: date, dateText:format(date, 'MM/DD/YYYY')});
};
handleDateInputChange = (event, value) => {
this.setState({dateText:value});
};
handleDateInputBlur = (value) => {
let parsedDate = parse(value, 'MM/DD/YYYY');
if(this.isADate(parsedDate)){
this.setState({selectedDate:parsedDate});
}
else{
this.setState({dateText:format(this.state.selectedDate, 'MM/DD/YYYY')});
}
};
isADate = (maybeDate) => {
if ( Object.prototype.toString.call(maybeDate) === "[object Date]" ) {
if ( isNaN( maybeDate.getTime() ) ) {
return false;
}
else {
return true;
}
}
else {
return false;
}
};
render(){
let dateInputWidth = "150px";
let datePickerMargin = "-185px";
return (
<div style={{display: "flex"}}>
<TextField
style={{width:dateInputWidth}}
value={this.state.dateText}
onChange={this.handleDateInputChange}
onBlur={(event) => this.handleDateInputBlur(event.currentTarget.value)}
/>
<IconButton style={{opacity:"0.65"}}
onClick={() => this.datePicker.focus()}>
<ActionDateRange />
</IconButton>
<div style={{width:"0px", height:"0px", marginLeft:datePickerMargin}}>
<DatePicker
id="dataPicker"
floatingLabelText={''}
value={this.state.selectedDate}
errorText={''}
disabled={false}
formatDate={date => { return format(date, 'MM/DD/YYYY') } }
autoOk
container="inline"
fullWidth
onChange={this.handleChangeDatePicker}
ref={c => {
this.datePicker = c
}}
/>
</div>
</div>
)
}
}
If you face the problem Cannot find prepareStyles of undefined error, please check if you have defined the theme as provider prior to any component usage, or else it won't work. Check this note:
Beginning with v0.15.0, Material-UI components require a theme to be provided. The quickest way to get up and running is by using the MuiThemeProvider to inject the theme into your application context.
And this is the sample snippet to show how to accomplish that:
In your App.js
import React from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import MyAwesomeReactComponent from './MyAwesomeReactComponent';
const App = () => (
<MuiThemeProvider>
<MyAwesomeReactComponent />
</MuiThemeProvider>
);
ReactDOM.render(
<App />,
document.getElementById('app')
);
And in your ./MyAwesomeReactComponent.js (that is the component you want to work witk):
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const MyAwesomeReactComponent = () => (
<RaisedButton label="Default" />
);
export default MyAwesomeReactComponent;
Please refer to their official usage guide for further details.
Look at this example:
https://mui.wertarbyte.com/#timepicker
You can replace the button in the example by a TextField with an icon and only when you click on the icon open a TimePicker instead of TimeInput or you can use the TimePicker of the material-ui-pickers package
Material-ui-pickers demo: https://material-ui-pickers.dev/api/timepicker#keyboard-input
I think the TimeInput component doesn't allow this, but you can write your own component to create the exact behavior you want. Instead of importing TimeInput import { TimePicker } from the package and create a custom component.
This is in no way fool proof but it will give you the basics to continue.
Working example: https://codesandbox.io/embed/5l167pzrx
import React, { useState } from "react";
import { Button, Input, InputAdornment, IconButton, Dialog, DialogActions } from '#material-ui/core';
import { TimePicker } from 'material-ui-time-picker';
import AccessTime from '#material-ui/icons/AccessTime';
function CustomDatePicker() {
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState('10:10');
const openDialog = () => setIsOpen(true);
const closeDialog = () => setIsOpen(false);
const handleDialogTimeChange = (newValue) => {
const hours = newValue.getHours().toString().padStart(2, "0");
const minutes = newValue.getMinutes().toString().padStart(2, "0")
const textValue = hours + ':' + minutes;
setValue(textValue);
}
const handleKeyboardTimeChange = (event) => setValue(event.target.value);
const createDateFromTextValue = value => {
const splitParts = value.split(':');
return new Date(1970, 1, 1, splitParts[0], splitParts[1]);
}
return (
<div>
<Input
value={value}
onChange={handleKeyboardTimeChange}
endAdornment={
<InputAdornment position="end">
<IconButton onClick={openDialog}>
<AccessTime />
</IconButton>
</InputAdornment>
}
/>
<Dialog maxWidth='xs' open={isOpen}>
<TimePicker mode='24h' value={createDateFromTextValue(value)} onChange={handleDialogTimeChange} />
<DialogActions>
<Button onClick={closeDialog} color='primary'>
Cancel
</Button>
<Button onClick={closeDialog} color='primary'>
Ok
</Button>
</DialogActions>
</Dialog>
</div>
)
}
export default CustomDatePicker
You have 2 options to do this:
use <KeyboardTimePicker />
use <KeyboardDateTimePicker />

Categories