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 ?
I've forked your sandbox and made a couple of adjustments. Though I've not fixed it - I'll just show you what's currently wrong.
https://codesandbox.io/s/j24rqql9n9
I modified two lines
In your constructor, I added
this.handleKeyboardTimeChange = this.handleKeyboardTimeChange.bind(this)
And your handleKeyboardTimeChange:
handleKeyboardTimeChange(event) {
this.setState({ time: event.target.value });
}
This simply sets the state to exact value passed in from what you see there. No additional validation.
Related
I have a MUI slider where the value of the slider needs to be inverted based on a boolean variable. My current code inverts it correctly but it also inverts the drag functionality, meaning if I tried to drag my slider towards 0, it would move towards 100 instead. I can't think of a workaround to correct this. Would appreciate if anyone has any suggestions I could try.
const handleSelectionChange = (e: any) => {
if (typeof e.target.value === 'string') {
onChange(e.target.value)
} else {
onChange(Number(e.target.value))
}
}
<Slider
aria-label='DiceRoll'
defaultValue={50}
valueLabelDisplay='auto'
step={1}
min={0}
max={100}
value={overUnder ? 100 - targetNumber : targetNumber} //overUnder is boolean and targetNumber ranges from 0 to 100.
marks={marks}
onChange={handleSelectionChange}
/>
Using the valueLabelFormat function, you can simply format the number display instead of the number value itself, preventing the problem.
Use this instead:
valueLabelFormat={(v) => (overUnder ? 100 - v : v)}
Set the value back to normal:
value={targetNumber}
const { StrictMode, useState } = React;
const { createRoot } = ReactDOM;
const App = () => {
const [overUnder, setOverUnder] = useState(false);
const [targetNumber, setTargetNumber] = useState(42.0);
const handleSelectionChange = (e) => {
if (typeof e.target.value === "string") {
setTargetNumber(e.target.value);
} else {
setTargetNumber(Number(e.target.value));
}
};
const onFlip = () => {
setOverUnder((s) => !s);
};
return (
<div>
<div>
<button onClick={onFlip}>Flip</button>
</div>
<div>Target Number: {targetNumber}</div>
<div>Over Under: {`${overUnder}`}</div>
<MaterialUI.Slider
aria-label="DiceRoll"
defaultValue={50}
valueLabelDisplay="on"
step={1}
min={0}
max={100}
value={targetNumber}
valueLabelFormat={(v) => (overUnder ? 100 - v : v)}
marks={false}
inverted={true}
onChange={handleSelectionChange}
sx={{ pt: "10rem" }}
/>
</div>
);
}
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/#mui/material#latest/umd/material-ui.production.min.js"></script>
Also a sandbox for reference.
So after a few hours I've found the solution, it's actually a pretty simple solution, just an additional 3 lines of codes. We just have to introduce another state which only purpose is to display the correct slider Value.
const handleSelectionChange = (e: any) => {
if(overUnder){ //defaultValue of overUnder is true
if (typeof e.target.value === 'string') {
onChange(e.target.value)
} else {
onChange(Number(e.target.value))
}
} else {
if (typeof e.target.value === 'string') {
onChange(100 - e.target.value)
setFlippedTargetNumber(e.target.value)
} else {
onChange(100 - Number(e.target.value))
setFlippedTargetNumber(Number(e.target.value))
}
}
}
const [ flippedTargetNumber, setFlippedTargetNumber] = useState<number>(50)
<Slider
aria-label='DiceRoll'
defaultValue={50}
valueLabelDisplay='auto'
step={1}
min={0}
max={100}
value={overUnder ? targetNumber : flippedTargetNumber} //overUnder is boolean and targetNumber ranges from 0 to 100.
marks={marks}
onChange={handleSelectionChange}
/>
basically what this does is, when overUnder = true, and when it's true, the slider would just work normally. when it is flipped and overUnder = false, onChange will update targetNumber to inverse [which is needed for other components]. and setFlippedTargetNumber will be used as slider Value instead of targetNumber.
Hope this helps anyone who is facing a similar problem. :)
I am trying to build a form in React with Bootstrap. I have two dates (one is when you started to work and the other is when you ended to work for company), the second one shouldn't be before the first one. So if the first one is 10.1.2020, you shoudn't be able to choose the date in the second one that is before this date.
As is a react app, I am also giving you a link: https://codesandbox.io/s/friendly-worker-nwtgu?file=/package.json
Code that I am interested in is inside these components: Practical and PracticalForm.
Code of form:
<Form.Group controlId="formBasicDateFrom">
<Form.Label>Worked from</Form.Label>
<Form.Control
onChange={(e) => {
checkDate(e);
handleChange(e);
}}
type="date"
defaultValue={dateFrom}
name="dateFrom"
required
/>
</Form.Group>
<Form.Group controlId="formBasicDateUntil">
<Form.Label>Worked to</Form.Label>
<Form.Control
isInvalid={validate}
onChange={(e) => {
checkDate(e);
handleChange(e);
}}
// onChange={(e) => {
// handleChange(e);
// checkDate();
// }}
type="date"
defaultValue={dateTo}
name="dateTo"
required
/>
<Form.Control.Feedback type="invalid">
Date must be later then date from when you worked...
</Form.Control.Feedback>
</Form.Group>
And the function I validate with, now looks like this:
checkDate = (event) => {
let d1 = new Date(this.state.dateFrom);
let d2 = new Date(this.state.dateTo);
if (d1.getTime() > d2.getTime()) {
this.setState({
validate: false,
});
} else {
this.setState({
validate: true,
});
}
console.log(d1.getTime());
console.log(d2.getTime());
console.log(this.state.validate);
};
You can move the checkDate(event) function inside this.setState of handleChange function because the things acts async here so while setting the state you can call the checkDate(event) like,
handleChange = (event) => {
this.setState(
{
[event.target.name]: event.target.value
},
() => {
this.checkDate(event);
}
);
};
Then modify the checkDate function like,
checkDate = async (event) => {
let d1 = new Date(this.state.dateFrom);
let d2 = new Date(this.state.dateTo);
if (d1.getTime() < d2.getTime()) {
this.setState({
validate: false
});
} else {
this.setState({
validate: true
});
}
console.log(d1.getTime());
console.log(d2.getTime());
console.log(this.state.validate);
};
Also change the condition,
d1.getTime() > d2.getTime() to d1.getTime() < d2.getTime() as d1 date should be lesser than d2 date always...
I am learning React Hooks, and I wanna to build a type race game: a mini game to track how quickly you can type out a snippet of text.
The logic is simple: When user starts the game, set the start time, and when the value of the input equals the chosen snippet, display the endTime.
But when I start the game and input the correct words, it doesn't show that I have input the same words.
Open the console, input any words, and I found that react did not capture the last letter I have input, for example, I input abc, the console just show me ab.
So how can I use react hooks to complete the game?
Here is my whole code:
const App = () => {
const SNIPPETS = [
'Bears, beets, battlestar galactica',
"What's Forrest Gump's password? 1Forrest1",
'Where do programmers like to hangout? The Foo Bar'
]
const INITIAL_GAME_STATE = { victory: false, startTime: null, endTime: null }
const [snippet, setSnippet] = useState('')
const [gameState, setGameState] = useState(INITIAL_GAME_STATE)
const [userText, setUserText] = useState('')
useEffect(() => {
console.log('change')
if (gameState.victory) {
document.title = gameState.victory ? 'Victory!' : 'Playing...'
}
}, [gameState.victory])
const updateUserText = e => {
setUserText(e.target.value)
console.log(snippet)
console.log(userText)
if (userText === snippet) {
console.log('same')
setGameState({
...gameState,
victory: true,
endTime: new Date().getTime() - gameState.startTime
})
}
}
const chooseSnippet = snippetIndex => () => {
setSnippet(SNIPPETS[snippetIndex])
setGameState({
...gameState,
startTime: new Date().getTime()
})
}
return (
<div className="game-wrapper">
<h2>Type Race</h2>
{ snippet && (
<>
<h4>{ snippet }</h4>
<input type="textarea" value={ userText } onChange={ updateUserText } />
<br />
</>
) }
<br/>
<button onClick={ chooseSnippet(0) }>Start a new race!</button>
<br/>
{ gameState.victory && (
<h4>`Done! <span>🎉</span>Time: ${ gameState.endTime }ms`</h4>
) }
<br/>
{
SNIPPETS.map((SNIPPET, index) => (
<button onClick={ chooseSnippet(index) } key={ index }>
{ SNIPPET.substring(0, 10) }...
</button>
))
}
</div>
)
}
Because React setState is asynchronous, so your userText value will not be updated after you call setUserText. To get an updated userText, wait for the next rendering of the component.
useEffect(() => {
if (userText === snippet) {
// do something ...
}
}, [userText]);
For more information: useState set method not reflecting change immediately
just as Trà Phan answered, setState is asynchronous in hooks too.
so I fixed it:
useEffect(() => {
if (userText === snippet && userText) {
setGameState({
...gameState,
victory: true,
endTime: new Date().getTime() - gameState.startTime
})
}
}, [userText])
i'm using this plugin in my project.
https://reactdatepicker.com
There is have some prop showTimeSelect this prop takes boolean value and hide or show time picker.
I'm trying to give option to user about selecting time picker, so i tried to make some onClick event and make this prop conditional.
But it's work sometimes, sometimes not..
I don't understand where is the problem here is my code:
import React from "react";
import ReactDOM from "react-dom";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "./styles.css";
class App extends React.Component {
state = {
startDate: new Date()
};
handleChange = date => {
this.setState({
startDate: date,
showTime: false
});
};
showTimeSelection = () => {
this.setState({
showTime: !this.state.showTime
});
};
render() {
return (
<div>
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange}
showTimeSelect={this.state.showTime}
>
{" "}
<div>
<a onClick={() => this.showTimeSelection()}>
TOGGLE TIME SELECTION
</a>
</div>{" "}
</DatePicker>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
and here is the codesandbox example
You can try on codeSandBox it's sometimes work directly sometimes you need to click outside of datepicker and click inside again.
I have noticed it only works if showTimeSelect is true before the DatePicker is going to be displayed. So, before closing DatePicker you have to set showTimeSelect to true. you can do it in prop onClickOutside
state = {
startDate: new Date(),
showTime: true
};
handleChange = date => {
this.setState({
startDate: date
});
};
showTimeSelection = () => {
this.setState({
showTime: !this.state.showTime
});
};
render() {
const { startDate, showTime } = this.state;
return (
<div>
<DatePicker
selected={startDate}
onChange={this.handleChange}
showTimeSelect={showTime}
onClickOutside={() => this.setState({ showTime: true })}
>
<div onClick={this.showTimeSelection} style={{ cursor: "pointer" }}>
<h4>TOGGLE TIME SELECTION</h4>
</div>
</DatePicker>
</div>
);
}
}
check out code sandbox . check out two other useful props onCalendarClose and onCalendarOpen
I'm using the prop "popup" for react-big-calendar and I'm getting the above error in the console upon clicking the "+x more" link. My onEventClick function works by routing me to "calendar/" but I'm not sure what I'm missing here. I read through some documentation but couldn't find the right example to solve the issue. I suspect it has something to do with "moment," or improper formatting of the date object.
import React from "react";
import BigCalendar from "react-big-calendar";
import moment from "moment";
import "react-big-calendar/lib/css/react-big-calendar.css";
const localizer = BigCalendar.momentLocalizer(moment);
let allViews = Object.keys(BigCalendar.Views).map(k => BigCalendar.Views[k]);
class Calendar extends React.Component {
onEventChange = event => {
this.onEventClick(event);
};
onEventClick = event => {
this.setState({
endDate: moment(event.end.toLocaleString()).format("YYYY-MM-DDTHH:mm"),
startDate: moment(event.start.toLocaleString()).format("YYYY-MM-DDTHH:mm"),
eventName: event.name
});
this.props.history.push("calendar/" + event.eventId);
};
render () {
function Event({ event }) {
return (
<span id="eventTitle">
{event.number +
" - " +
event.name}
</span>
);
}
return(
<React.Fragment>
<div style={{ height: 700 }}>
<BigCalendar
localizer={localizer}
toolbar={true}
events={this.state.events}
popup
selectable
onSelectSlot={(slotInfo) => alert(
`selected slot: \n\nstart ${slotInfo.start.toLocaleString()} ` +
`\nend: ${slotInfo.end.toLocaleString()}`
)}
views={allViews}
components={{
event: Event
}}
onSelectEvent={event => this.onEventChange(event)}
eventPropGetter={(event) => {
let newStyle = {
backgroundColor: "",
color: "white",
borderRadius: "5px",
border: "none"
};
if (event.eventStatusCd === "CL ") {
newStyle.backgroundColor = "firebrick";
}
return {
className: "",
style: newStyle
};
}}
/>
</div>
</React.Fragment>
);
}
}
export default Calendar;
Here's a sample from the "start" property for an event object.
This object gets pushed into an array of other event objects:
eventObj.start = new Date(
Date.parse(
moment
.utc(dateCreated)
.local()
.format("YYYY-MM-DDTHH:mm")
)
);
I ran into similar issues, when first implementing. While react-big-calendar uses an internal localizer (in your case it's probably Moment), it still expects that, in the events that you provide it, all start and end dates must be valid JS Date Objects. My suggestion is, rather than running Date.parse(), you do something like:
eventObj.start = moment(dateCreated).local().toDate(); // returns valid JS Date Object