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} />
Related
I am receiving an error of "HomePage.jsx:16 Uncaught TypeError: elements.map is not a function" when trying to change the boxtitle for the array component. I have tried moving around functions and such but nothing seems to work.
I basically only want to change the title for the certain array object with the same boxid. Below is my code.
HomePage.jsx:
import react from 'react';
import { useEffect, useContext } from 'react';
import '../App.css';
import Todobox from './Todobox';
import { ElementContext } from '../ElementContext';
export default function HomePage(){
const { elements, setElements, newElement, elementId } = useContext(ElementContext);
return(
<div className='page-container'>
<div className='header'>
<a className='header-title'>Trello Clone!</a>
<a className='header-button' onClick={newElement}>Create a list</a>
</div>
<div className='element-field'>
{elements.map((element) => <Todobox key={element.boxid} boxid={element.boxid} boxtitle={element.boxtitle}/>)}
</div>
</div>
)
}
Todobox.jsx:
import React from 'react';
import Item from './Item';
import { useState, useContext } from 'react';
import '../App.css';
import { ElementContext } from '../ElementContext';
export default function Todobox({ boxtitle, boxid }){
const { elements, setElements } = useContext(ElementContext);
const [boxheader, setBoxHeader] = useState();
const handleSubmit = (e) => {
const object = elements.find(obj => {
if (obj.boxid === boxid){
setBoxHeader(e.target.value)
return obj
}})
setElements({...object, boxtitle: boxheader})
}
const handleKeydown = (e) => {
if(e.keyCode == 13 && e.shiftKey == false){
e.preventDefault();
handleSubmit(e)
}
}
return(
<div className='element-box'>
<textarea className='element-title-input' placeholder={boxtitle} onKeyDown={handleKeydown}/>
<Item />
<textarea
className='element-input'
type='text'
placeholder={`Add item... ${boxid}`}
onClick={() => {console.log(boxid)}}
/>
</div>
)
}
ElementContext.js:
import React, { createContext, useState } from 'react';
import Todobox from './components/Todobox';
export const ElementContext = createContext();
export const ElementContextProvider = ({children}) => {
const [elements, setElements] = useState([]);
const [elementId, setElementId] = useState(1);
const [title, setTitle] = useState('Add title...');
const [refDict, setRefDict] = useState({});
const newElementId = (elements) =>{
setElementId(elementId + 1);
console.log(elementId)
}
const newElement = () => {
newElementId();
if (!refDict[elementId]) {
setElements(prev => [...prev, { boxtitle: title, boxid: elementId }]);
setRefDict((prev) => ({...prev, [elementId]: true}));
}
console.log(elements);
};
const value = {
elements,
setElements,
newElement,
elementId,
};
return(
<ElementContext.Provider value={value}>
{children}
</ElementContext.Provider>
)
};
Code Sandbox
Any help is appreciated since I am new and still learning! :)
Few points to handle
const newElement = () => {
newElementId(); // this wont update as react batches the state updates
// try using userRef for elementId
// create new element id here, and then set it
// const newId = elementId + 1
// setElementId(newElementId)
if (!refDict[elementId]) {
setElements(prev => [...prev, { boxtitle: title, boxid: elementId }]);
setRefDict((prev) => ({...prev, [elementId]: true}));
}
console.log(elements);
};
on submit
const object = elements.find(obj => {
if (obj.boxid === boxid){
setBoxHeader(e.target.value) // wont update the as React will likely batch the state updates
return obj
}})
setElements({...object, boxtitle: boxheader}) // is this an array ?
instead try
const object = elements?.find(obj => obj.boxid === boxid)
if (object) {
setBoxHeader(e.target.value)
setElements([ object, { boxtitle: e.target.value, boxId: elementId } ]) // what is the structure of array elements
}
you will find the new React documentation about updating arrays useful
I'm making an interactive graph which re-renders when you select a different date. It used to re-render fine before I made some fundamental changes (moved state responsibilites to the components instead of app.js. When I change the date, it does seem to actually change the url in the component responsible for making the api call. However, it's not re-rendering even though it's in a useEffect, and it did re-render before.
I'm new to React so it might be something very obvious. I'm also not sure if this is the right way to do things so I'm open for any pointers to improve my application or best practices.
App.js: Preferably I'd remove the useGetData and useURLSwitcher in the app.js, but without them it's not able to load the url on start up.
import "./App.css";
import React, { useEffect, useState } from "react";
import { Button } from "./components/button/Button";
import useGetData from "./apiData/useGetData";
import BarChart from "./components/BarChart";
import { SpinnerDiamond } from "spinners-react";
import GraphChart from "./components/GraphChart";
import { DatePicker } from "./components/datePicker/DatePicker";
import useURLSwitcher from "./apiData/useURLSwitcher";
import { DatePickerWithButton } from "./components/datePickerWithButton/DatePickerWithButton";
const App = () => {
const {url} = useURLSwitcher()
const { dataNew, isLoading, error } = useGetData(url);
console.log(url)
return isLoading ? (
<div className="spinner">
<SpinnerDiamond
size={400}
speed={100}
secondaryColor="#354A54"
color={"#00BAEC"}
/>
</div>
) : error ? (
console.log(error)
) : (
<div className="app">
<div className="example">
<div className="graph-box">
<div id="wrapper">
<GraphChart graphData={dataNew} />
</div>
<div className="side-bar">
<DatePicker
nameOne={"Start Date"}
nameTwo={"End Date"}
/>
<Button name={"Apply"}/>
</div>
</div>
<div className="graph-box">
<div id="wrapper">
<BarChart graphData={dataNew} />
</div>
<div className="side-bar">
<DatePickerWithButton
nameOne={"Start Date"}
nameTwo={"End Date"}
name={"Apply"}
/>
</div>
</div>
DatePickerWithButton: These used to be two separate components which I combined to make it easier to set the start,- and end dates.
import React, { useState } from "react";
import "./DatePickerWithButton.css";
import useURLSwitcher from "../../apiData/useURLSwitcher";
import useGetData from "../../apiData/useGetData";
import { format, parseISO } from "date-fns";
export const DatePickerWithButton = ({
nameOne,
nameTwo,
name,
}) => {
let tempStartDate ="";
let tempEndDate= "";
const [startDate, setStartDate] = useState();
const [endDate, setEndDate] = useState();
const {url} = useURLSwitcher(startDate, endDate)
const onClick = () => {
setStartDate(tempStartDate);
setEndDate(tempEndDate);
}
return (
<>
<div className="date-box">
<label className="styleDate">{nameOne}</label>
<input
className="datePicker"
type="date"
onChange={(e) => tempStartDate = e.target.value}
></input>
</div>
<div className="date-box">
<label className="styleDate">{nameTwo}</label>
<input
className="datePicker"
type="date"
onChange={(e) => tempEndDate= e.target.value}
></input>
</div>
<button className="button-28" onClick={()=> onClick()}>
{name}
</button>
{console.log("start date " + startDate, " end date "+ endDate)}
</>
);
};
The custom hook I made to fetch the data:
import { useState, useEffect } from "react";
import axios from "axios";
const config = {
"Content-Type": "application/json",
Accept: "application/json",
};
const useGetData = (url) => {
const [dataNew, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => {
const { data } = await axios({
url: url,
method: "GET",
data: null,
withCredentials: true,
headers: config,
}).catch((err) => setError(err));
console.log(url)
const newData = data.value.map((d) => {
const personId = d._personId;
const startDate =
d._startDate;
const endDate =
d._endDate;
return {
personId,
startDate,
endDate,
};
});
setData(newData);
setIsLoading(false);
}, [url]);
return {
dataNew,
isLoading,
error,
};
};
export default useGetData;
The second custom hook that should set the right url once selected with the DatePickerWithButton. Links have been pruned:
import { format, parseISO } from "date-fns";
import { isEmpty } from "lodash";
import useGetData from "./useGetData";
const useURLSwitcher = (setStartDate, setEndDate) => {
const startDate = setStartDate;
const endDate = setEndDate;
console.log(setStartDate);
console.log(setEndDate);
let url = "";
const RawThisYear = new Date();
const RawLastYear = new Date();
RawLastYear.setFullYear(RawLastYear.getFullYear() - 5);
const ThisYear = format(RawThisYear, "yyyy-dd-MM");
const LastYear = format(RawLastYear, "yyyy-dd-MM");
if (startDate === undefined && endDate === undefined) {
url = `http://192.168/{LastYear} lt ${ThisYear}`;
} else {
// if (startDate > endDate) {
// alert("Start date can't be greater than the end date!");
// } else {
if (startDate !== undefined && endDate !== undefined) {
url = `http://192.168/$filter={startDate}{endDate}`;
console.log(url)
}
}
console.log(url)
return {
url,
};
};
export default useURLSwitcher;
In general a component will re-render if its state or incoming props changes. The App.js is also a component.
At first glance, when you change a date in DatePickerWithButton, it does not seems updating any state/props that the parent App.js is aware of, hence no re-rendering.
The useURLSwitcher() just returns an object with url string.
The onClick function below only updates the DatePickerWithButton's local states.
const [startDate, setStartDate] = useState();
const [endDate, setEndDate] = useState();
const {url} = useURLSwitcher(startDate, endDate)
const onClick = () => {
setStartDate(tempStartDate);
setEndDate(tempEndDate);
}
The GraphChart component is a child component of App.js, but no state/props changes in App.js, hence, GraphChart does not re-render when only the DatePickerWithButton's local states changes.
How to render the component lazily, on the select of a value change.
Here is my below snippet, i am not able to see the title, nothing is rendering
App.js
import React, { useState } from "react";
import "./styles.css";
import { SectionOne, SectionTwo } from "./ChildComponents";
export default function App() {
const [state, setState] = useState(null);
const sectionToBeDisplayed = {
section_one: (title = "section one") => <SectionOne title={title} />,
section_two: (title = "section two") => <SectionTwo title={title} />
};
const handleSelectChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<select onChange={handleSelectChange}>
<option disabled selected value>
{" "}
-- select an option --{" "}
</option>
<option value="section_one">SectionOne</option>
<option value="section_two">SectionTwo</option>
</select>
{sectionToBeDisplayed[state]}
</div>
);
}
ChildComponents.js
import React from "react";
export const SectionOne = ({ title }) => {
return <h1>{title}</h1>;
};
export const SectionTwo = ({ title }) => {
return <h2>{title}</h2>;
};
So based on the selection only i need to load this component, I am newbie to react, read that we can use React.lazy but i don't see any use case like this. Also whenever i change it should not build the dom again. Should we use useMemo, I am not clear to use React.memo or useMemo, Which one is better.
You need to invoke the function component:
// Component is a function component
const Component = (title = "section one") => <SectionOne title={title} />;
// Invoke it
<Component/>
const sectionToBeDisplayed = {
section_one: ...
};
export default function App() {
...
const SectionComponent = sectionToBeDisplayed[state];
return (
<div className="App">
...
<SectionComponent />
</div>
);
}
I would like to update the parent state from child component, which renders each object of the array of objects. The main goal of the child component is to update the original value from the array of objects.
I've the following code
Parent:
import { useState } from 'react';
import ExpenseItem from './expenseItem';
function Update({ data }) {
const [ expenses, setExpenses ] = useState(data);
return (
<div>
{expenses.map((expense, index) => {
return <ExpenseItem key={index} {...expense} />;
})}
<button>Save</button>
</div>
);
}
export default Update;
child:
import { useState, useRef } from 'react';
function ExpenseItem({ description, date, credit, debit }) {
const [ edit, setEdit ] = useState(false);
const [ expenseDescription, setExpenseDescription ] = useState(description);
const textInput = useRef();
const renderDefaultView = () => {
return <h3 onDoubleClick={() => setEdit(true)}>{expenseDescription}</h3>;
};
const renderEditView = () => {
return (
<div>
<input
type="text"
ref={textInput}
defaultValue={expenseDescription}
onDoubleClick={() => setEdit(true)}
/>
<button onClick={() => setEdit(false)}>X</button>
<button onClick={() => updateValue()}>OK</button>
</div>
);
};
const updateValue = () => {
const value = textInput.current.value;
setExpenseDescription(value);
textInput.current.defaultValue = value;
setEdit(false);
};
return (
<div>
{edit ? renderEditView() : renderDefaultView()}
<span>{date}</span>
<p>{debit}</p>
<p>{credit}</p>
</div>
);
}
export default ExpenseItem;
Once way, is to pass the parent state property (expenses) and the function that updates it (setExpenses) to the child Component via the props:
Parent:
import React from 'react';
import ReactDOM from 'react-dom';
import { useState } from 'react';
import ExpenseItem from './ExpenseItem';
function Update({ data }) {
const [ expenses, setExpenses ] = useState(data);
return (
<div>
Checking: { expenses[0].description } | { expenses[1].description }
<hr/>
{expenses.map((expense, index) => {
return <ExpenseItem key={index} index={index} expenses={expenses} setExpenses={setExpenses} />;
})}
<button>Save</button>
</div>
);
}
export default Update;
Child:
import React from 'react';
import { useState, useRef } from 'react';
function ExpenseItem( props ) {
let { description, date, credit, debit } = props.expenses[props.index];
const setExpenses = props.setExpenses;
const [ edit, setEdit ] = useState(false);
const [ expenseDescription, setExpenseDescription ] = useState(description);
const textInput = useRef();
const renderDefaultView = () => {
return <h3 onDoubleClick={() => setEdit(true)}>{expenseDescription}</h3>;
};
const renderEditView = () => {
return (
<div>
<input
type="text"
ref={textInput}
defaultValue={expenseDescription}
onDoubleClick={() => setEdit(true)}
/>
<button onClick={() => setEdit(false)}>X</button>
<button onClick={() => updateValue()}>OK</button>
</div>
);
};
const updateValue = () => {
const value = textInput.current.value;
setExpenseDescription(value);
textInput.current.defaultValue = value;
setEdit(false);
const expenses = [ ...props.expenses ]; // Get a copy of the expenses array
// Replace the current expense item
expenses.splice( props.index, 1, {
description: value, date, credit, debit
});
// Update the parent state
setExpenses( expenses );
};
return (
<div>
{edit ? renderEditView() : renderDefaultView()}
<span>{date}</span>
<p>{debit}</p>
<p>{credit}</p>
</div>
);
}
export default ExpenseItem;
Working demo
This can get really complicated as you move along, so the best option is to look for some sort of State Management solution, like using the Context API.
Also, take a look at this interesting post that talks about using the map index value as a key value: Index as a key is an anti-pattern
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 />