react / classify date - javascript

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.

Related

How can I add favorite list on local storage React.js

I want to implement add-to favorite list functionality. But It only works on a single item I need multiple items. I use useRef Hook and use this npm package react-use-localstorage
The problem is my local storage doesn't work properly as I expected. It always updates a single item but I need it as an Array i.e [1, 2, 4, 7, 10]
If I reload my page only 3 number id will fill the heart 🤨
import React, { useRef } from "react";
import FavoriteBorder from "#mui/icons-material/FavoriteBorder";
import Favorite from "#mui/icons-material/Favorite";
import IconButton from "#mui/material/IconButton";
import useLocalStorage from 'react-use-localstorage';
const Fv = ({ id }) => {
const [storageItem, setStorageItem] = useLocalStorage(
"favourites",
JSON.stringify([])
);
//const storagedArray = useRef(JSON.parse(storageItem));
//const isFavourited = storagedArray.current.includes(id);
const isFavourited = storageItem.includes(id)
const handleToggleFavourite = () => {
if (!isFavourited) {
setStorageItem(JSON.stringify([...JSON.parse(storageItem), id]));
} else {
setStorageItem(
JSON.stringify(
JSON.parse(storageItem).filter((savedId) => savedId !== id)
)
);
}
return (
<IconButton onClick={handleToggleFavourite}>
{isFavourited ? <Favorite color="error" /> : <FavoriteBorder color="error" />}
</IconButton>
);
};
export default Fv;
Assign Component
<Fv id={product.id} />
Okay, to be perfectly honest, I don't know why your array behaves like that. Maybe it is this external library malfunctioning. You don't need to use any external libraries to manage your favourites state. Here is a solution without react-use-localstorage:
import React, { useState } from "react";
import FavoriteBorder from "#mui/icons-material/FavoriteBorder";
import Favorite from "#mui/icons-material/Favorite";
import IconButton from "#mui/material/IconButton";
const Fv = ({ id }) => {
const [storageItem, setStorageItem] = useState(() => JSON.parse(localStorage.getItem("favourites") || "[]"))
const isFavourited = storageItem.includes(id)
const handleToggleFavourite = () => {
if (!isFavourited) {
const newStorageItem = [...storageItem, id]
setStorageItem(newStorageItem);
localStorage.setItem("favourites", JSON.stringify(newStorageItem))
} else {
const newStorageItem = storageItem.filter((savedId) => savedId !== id)
setStorageItem(newStorageItem);
localStorage.setItem("favourites", JSON.stringify(newStorageItem))
}
return (
<IconButton onClick={handleToggleFavourite}>
{isFavourited ? <Favorite color="error" /> : <FavoriteBorder color="error" />}
</IconButton>
);
};
export default Fv;

How to instantly display data after an API call in react hooks

I don't understand why the second line, which reads data from the props, is not displayed as instantly as the first, i would like them to be displayed instantly
I update the state when a button is clicked, which calls api, data is coming in, the state is updating, but the second line requires an additional press to display
How to display both lines at once after a call? What's my mistake?
I'm using react hooks, and i know that required to use useEffect for re-render component, i know, that how do work asynchronous call,but i'm a little confused, how can i solve my problem, maybe i need to use 'useDeep effect' so that watching my object properties, or i don't understand at all how to use 'useEffect' in my situation, or even my api call incorrectly?
I have tried many different solution methods, for instance using Promise.all, waiting for a response and only then update the state
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./test";
ReactDOM.render(<App />, document.getElementById("root"));
app.js
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const useDataApi = (initialState) => {
const [state, setState] = useState(initialState);
const stateCopy = [...state];
const setDate = (number, value) => {
setState(() => {
stateCopy[number].date = value;
return stateCopy;
});
};
const setInfo = async () => {
stateCopy.map((item, index) =>
getFetch(item.steamId).then((res) => setDate(index, res.Date))
);
};
const getFetch = async (id) => {
if (id === "") return;
const requestID = await fetch(`https://api.covid19api.com/summary`);
const responseJSON = await requestID.json();
console.log(responseJSON);
const result = await responseJSON;
return result;
};
return { state, setState, setInfo };
};
const Children = ({ data }) => {
return (
<>
<ul>
{data.map((item) => (
<li key={item.id}>
{item.date ? item.date : "Not data"}
<br></br>
</li>
))}
</ul>
</>
);
};
const InfoUsers = ({ number, steamid, change }) => {
return (
<>
<input
value={steamid}
numb={number}
onChange={(e) => change(number, e.target.value)}
/>
</>
);
};
function App() {
const usersProfiles = [
{ date: "", id: 1 },
{ date: "", id: 2 }
];
const profiles = useDataApi(usersProfiles);
return (
<div>
<InfoUsers number={0} change={profiles.setID} />
<InfoUsers number={1} change={profiles.setID} />
<button onClick={() => profiles.setInfo()}>Get</button>
<Children data={profiles.state} loading={profiles} />
</div>
);
}
export default App;
To get the data, just click GET
In this example, completely removed useEffect, maybe i don’t understand how to use it correctly.
P.s: Sorry for bad english
You don't need stateCopy, as you have it in the callback of the setState:
const setInfo = async () => {
// we want to update the component only once
const results = await Promise.all(
state.map(item => getFetch(item.steamId))
);
// 's' is the current state
setState(s =>
results.map((res, index) => ({ ...s[index], date: res.Date })
);
};

How do i concat an array with a different array which uses of local storage

Datalist is an array I'm trying to concat the boards array with the Datalist array, but when I console it doesn't reflect. On the other hand when I assign Datalist.concat(boards) to a variable it reflects example
const newArr = Datalist.concat(boards);
console.log(newArr)
(main code) please help me review it. Thanks in advance
import React, { useState, useEffect } from 'react';
import Modal from './Modal';
import { Datalist } from '../Data/Boards';
function Boards() {
const [boards, setboards] = useState(JSON.parse(localStorage.getItem('boards')) || []);
const [title, settitle] = useState('');
localStorage.setItem('boards', JSON.stringify(boards));
Datalist.concat(boards);
console.log(Datalist);
const handleChange = (e) => {
settitle(e.target.value);
};
const handleSubmit = () => {
if (title.length === 0) {
return;
}
setboards((prev) => [...prev, title]);
};
return (
<div>
<ul id="boards">
<BoardList boards={boards} />
</ul>
<Modal title={title} handleChange={handleChange} handleSubmit={handleSubmit} />
</div>
);
}
function BoardList({ boards }) {
const history = useHistory();
return (
<>
{boards.map((board, index) => (
<li
key={index}
onClick={() => {
history.push('./workspace');
}}
>
<h3>{board}</h3>
</li>
))}
</>
);
}
export default Boards;
That is the expected behaviour. The concat function does not alter the original arrays. You can read about it in the MDN docs
For your case you should be able to do Datalist = Datalist.concat(boards); and it should work like you're expecting

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} />

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