I have implemented the datepicker and timepicker with add,delete buttons in each row. When I click on add, will add new row and delete will delete row.
I have the code link https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
But how to handle the state for multiple datepicker and timepicker,
When change the date, it doesnot reflect the change in field.
https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
So the problem is in your renderRowData function:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state.pickdate}
/>
)
For value u use this.state.pickdate, but when value changes you set it with:
handleDatePicker = (value, name, field, row) => {
this.props.handleInputChange(value, field, row);
console.log("data", value, "for", name);
this.setState({ [name]: value });
};
wich means that your state is now:
{
["pickdate_" + rowId]: value // where row is selected row
}
you need to change your datepicker to access value like this:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state["pickdate_" + rowId] || this.defaultPickDate} // this will take new selected value or default if there is none
/>
)
Working example: https://codesandbox.io/s/funny-fog-1v9o3
The reason that the date field's changes aren't reflected in the UI is that you've implemented it as a controlled component (you are setting the value of each DatePicker based on the corresponding value in the state and updating the state when you change the value, which effectively synchronizes the component with the state, more or less), whereas the TimePickers do change in the UI when a new time is chosen because they are implemented as uncontrolled components. Controlled components are often the best method, but your date component isn't updating on change because there are problems in your handleDatePicker function, as pointed out by Kaca992's answer.
If you don't know much about controlled vs uncontrolled components, see here.
As for how to handle the state for multiple DatePickers and TimePickers, personally I'd recommend that you store them as an array of rows in the state. For example, this would be a default state:
this.state = {
rows: [
{
date: new Date(),
start: moment(),
end: moment(),
}
]
}
Each row element in the array would correspond to a row in the table, and you can just use array.map to render each row as a component that contains the DatePicker, TimePickers, and buttons, and then just send the array index along with the new value to your onChange functions so that the correct row's changes can be reflected in the new state.
This would require a bit of a re-write of your DynamicDateTimePicker class, but the logic would be much simpler and more readable than how you currently have it structured.
Related
We are using antd for datepicker and moments as util. I'm stuck for a week in this ISSUE.The thing is, in the filter sidepanel,on pressing clear,all the fields should clear or set to their default values(in case of dropdown).But the date picket is not resetting.
The above picture is the Activity component and left side to its his the filter.A basic filter with API change from backend on every action event.
useEffect(()=>{
if(clearFilter){
form.resetFields()
setActivitySearchText('')
setFromDate('')
setToDate('')
setStatusSearchText('')
onStatusChange('')
setClearFilter(false)
}
},[clearFilter])
const onChangeFromDate = dateString => {
setFromDate(new Date(dateString).toISOString())
}
const onPageToDate = dateString => {
setToDate(new Date(dateString).toISOString())
}
<StyledDatePicker
allowClear={false}
format={dateFormat}
disabledDate={disabledFromDate}
placeholder={'From'}
onChange={(fromdate, dateString) =>
onChangeFromDate(fromdate, dateString)
}
showTime={{
use12Hours: true,
defaultValue: moment('00:00:00', 'HH:mm:ss'),
}}
/>
<StyledDatePicker
format={dateFormat}
disabledDate={disabledToDate}
placeholder={'To'}
onChange={(todate, dateString) => onPageToDate(todate, dateString)}
showTime={{ use12Hours: true }}
/>
The above code is the index file for all the components,we'll be passing clearfilter prop,if its true ,the filter components are set to empty.The StyledDatePicker is just wrapped in styled components of some custom width.that's it.
You can clearly see,onChangeFromDate() and onPageToDate() are the event functions happening on Change,onChange. As I said above,I'm setting the setFromDate('') and setTodate('') when clearFilter is true.
To give some context,this another main file,from which the props are passed to the others.In there,we are defining setFromDate('') and setTodate('') as,
const [fromDate, setFromDate] = useState('')
const [toDate, setToDate] = useState('')
I think I've given enough details. If need anything, request, I'm ready to give. This is a live project, I'm stuck for a week.Thanks in advance!
I have a dropdown where and i run a function onChange. After onChange i am filtering the current React state. This works, and i am able to filter on a unique value. However, i am not able to reset the state to the previous original state on change after switching to another dropdown select item.
handleStateOnChange = e => {
let selectedWerkgever = e.target.value;
const list = this.state.opleidingen.filter(({ opleiding_werkgever }) =>
selectedWerkgever.includes(opleiding_werkgever)
);
this.setState({
opleidingen: list,
isResetButtonActive: true,
});
console.log('changed');
};
I am filtering on everything inside the array that includes "opleiding_werkgever". But how can i first revert back on change, and re-filter again?
Switching to another dropdown on the same component or in the components sharing same state does not automatically reset the state. Your first call on handleStateChange filters the state and it will remain so until that component is unmounted. You could decide to retain the original opleidingen and then use it to reset the opleidingen when needed.
{
opleidingen: list,
isResetButtonActive: true,
originalOpleidingen : list
}
I would store selectedWerkgever in state and use that to filter your drop down elements. Keep the original list intact.
So simplify your handleStateOnChange:
handleStateOnChange = e => {
this.setState({
selectedWerkgever: e.target.value,
isResetButtonActive: true
});
};
And use this to filter your dropdown options:
<select>
{this.state.opleidingen.filter(({ opleiding_werkgever }) =>
this.state.selectedWerkgever.includes(opleiding_werkgever)).map(item=>
<Option value={item} />}>)
}
...
Trying to build a react component where I need to control checked status of checboxes and select options when change event occurs. But I don't know how it is possible to get value of the checked checkbox(es) and set the state.
We're using custom data-binding. On page load, we're assigning selected value of the select, with jQuery.
Programmatically changing value of the select must update matching check-boxes.
When user checks/unchecks a checkbox, corresponding value must be toggled on the select.
With jQuery I would loop trough check-boxes and build array with checked values then assign this value to the select on checkbox change. And when select change event is triggered, I would uncheck all check-boxes and check the ones matching selected items.
This is my simplified code.
state = {
items: [
{Key: 1, Value: "A"},
{Key: 29, Value: "Z"}
],
selected: [1, 29]
}
function onSelectChange(){
// Update checked checkboxes
}
function onCheckboxChange(){
// Update selected options
}
<div>
<select multiple onChange={onSelectChange} className="hidden">
{this.state.items.map((item, i) =>
<option value={item.Key}>{item.Value}</option>
)}
</select>
<div className="checkboxes">
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange} />
)}
</div>
</div>
You would use this.setState({}) inside the event handler to update the state of a component in React. This triggers a rerender in React which allows you to query the the updated state (this.state.selected).
Be advised that this.setState() expects an immutable object, so you should never change the previous, but always set a new state object!
Answer to comment:
For selectItem:
onSelectChange = event => this.setState({selected:event.target.value})
and for checkboxes (note the prevState):
onCheckboxChange = item => event => this.setState(({selected,...prevState})=> ({
...prevState,
selected: event.target.checked? selected.concat(item): selected.filter(it=> it!== item)
}))
and usage:
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange(item)} />
)}
This has the downside that it will create a new function on each rerender, so it's better to create a custom CheckboxItem and pass the item to it and use a handleClick.
onChange function give event where you could check whether the select box is being checked or not using this you can update your state accordingly.
function onCheckboxChange(e){
console.log("checked", e.target.checked);
// Then, on the basis of boolean you can update your state
}
This is my Creatable component:
function optionsForSelect(field) {
return field
.values
.map((fieldOption) => {
return {value: fieldOption, label: fieldOption};
});
}
function PatientSelectInput({field, options, value, onChange, disabled}) {
const className = field.id + '-select';
return (
<label className={cx('input-label', className)}>
<div className="label-text">{field.displayName}</div>
<Creatable
value={value}
onChange={(selectedValue) => onChange(selectedValue ? selectedValue.value : null)}
disabled={disabled}
onBlurResetsInput={false}
onCloseResetsInput={false}
options={options} />
</label>
);
}
It is a functional component. When it renders, I can create a new option but when I hit tab or enter or click on the automatically generated "Create Option..." the newly created option disappears. I just want the default behavior.
What am I missing?
Unfortunately this version of react-select mutates it's options prop.
This is very bad coding and is fixed in version 2: https://github.com/JedWatson/react-select/issues/2484
The issue you're experiencing is coming from the way you construct the options. Your function creates a new object on each render, replacing the array containing the option you just created.
If you can't update your version I suggest you save the output of optionsForSelect to this.state.options, then pass the state variable into react-select. The upshot of doing this is you still have the ability to mutate the state of your element and limits the impact of the mutate.
I have a Materialize input like so:
<input type="date" className="datepicker" onChange={this.props.handleChange} />
It is being correctly initialised by Materialize, but not firing onChange when the value of the datepicker changes. What am I doing wrong here? This problem seems to extend to all Materialize inputs.
On componentDidUpdate() using a prop id
var elem = document.getElementById('date');
M.Datepicker.init(elem,{
onClose:()=>{
this.state.date = elem.value;
this.setState(this.state)
}
});
I'm pretty sure this solves the caveat if you put it in your componentDidMount component.
If the select is to be re-rendered on state change, this should as well be put in componentDidUpdate
// find the select element by its ref
const el = ReactDOM.findDOMNode(this.refs.ref_to_my_select);
// initialize the select
$('select').material_select();
// register a method to fireup whenever the select changes
$(el).on('change', this.handleInputChange)
To get the value of the datepicker in materialize they provide an onSelect option when initialising the component:
var instances = M.Datepicker.init(
elems,
{
onSelect:function(){
date = instances[0].date;
console.log(date);
}
}
);
https://codepen.io/doughballs/pen/dyopgpa
Every time you pick a date, onSelect fires, in this case console.logging the chosen date.
When you close the datepicker (which is actually a modal), that's when the onChange fires, in this case logging 'onChange triggered' to the console.
that's my solution. I use useRef hook, to identify datepicker input and when onClose is fired, we can capture the object and data value, through ref var.
import React, { useEffect, useState, useRef } from "react";
import M from "materialize-css";
export default function App() {
const fromref = useRef(null); //create reference
const [date, setDate] = useState({ fromdate: "" });
const { fromdate } = date;
useEffect(() => {
let elem = document.querySelectorAll(".datepicker");
M.Datepicker.init(elem, {
firstDay: true,
format: "yyyy-mm-dd",
onClose: function() { // when onclose datepicker, we can get the value and data through the ref
console.log(fromref.current.name, fromref.current.value);
setDate({ [fromref.current.name]: fromref.current.value });
}
});
}, []);
return (
<form class="col s12">
<div class="row">
<div class="input-field col s12">
<input
name="fromdate"
type="text"
class="datepicker"
placeholder="from date"
ref={fromref} //set reference to the input
/>
</div>
</div>
</form>
);
}
If you want to get the value or other attributes you can access them from instaces variable when initialized and then check before submitting your form.
var elems = document.querySelectorAll('.timepicker');
var instances = M.Timepicker.init(elems);
Then in order to get your value before submitting your form can do as follow:
var date = instances[0].el.value;
There are two things which might be stopping the execution of expected behaviour.
If the code which you have displayed question section is from
rendered html tree, then onchnage assigment needs to be called while
assignment itself.
<input type="date" className="datepicker" onChange=this.props.handleChange(event)/>
Note: Previously browser events use to expects event callback
handlers in string format as a value.
In MaterializeCss documentation there is no mentioning of onChange event, this means there cannot be direct way to get it.
https://materializecss.com/pickers.html
It looks like you're using materialize directly in your post but if it is possible, you could try using react-materialize as it wraps all the materialize components such that it's easier to use with React. Using react-materialize would probably be the cleanest way to handle state and event changes as they provide a convenience wrapper around each materialize component.
When using the date picker from react-materialize, you'll need to pass the handleChange method into the options prop like so:
<DatePicker
options={{
...,
onSelect: this.props.handleChange
}}
/>
In the case of using the materialize date picker independently, if you could provide more details on how you're initializing the date picker input, I could provide a more relevant answer. But I'll give it a shot in the dark.
From the materialize docs it looks like you'll also have to pass back some options when you initialize it to handle a callback function when a date is selected.
I've added a JSFiddle that has a working example as well as a code snippet below, notice that when you select a date, 'hello world' is logged in the console, and the date is the first argument passed into the callback.
class Datepicker extends React.Component {
constructor(props) {
super(props)
}
handleChange(date) {
console.log('hello world', date);
}
componentDidMount() {
var elems = document.querySelectorAll('.datepicker');
var instances = M.Datepicker.init(elems, {
onSelect: this.handleChange
});
}
render() {
return (
<div>
<input type="text" className="datepicker" />
</div>
)
}
}
Live Example Fiddle
So to answer your question of how to handle events and setting the state, you just need to pass your handleChange method into the provided options configs depending on how you're using materialize date picker. In regards to integrating with a form, you could use the other callback hooks like onClose to do form validation.