MUI DatePicker hangs on big date changes - javascript

MUI DatePicker (either from Labs or X) was working fine. I cannot work out what happened, but it now hangs if I want to change the date by ten years. It seems like the code goes into an infinite loop, but there's no output to the console. The code is
function DateSelector(props) {
const setValue = value => {
if (value !== null) {
const checked = checkDate(value);
props.handler(checked);
}
}
return (
<DatePicker
label={props.label}
inputFormat="d/M/y"
mask=""
value={props.date}
onChange={(value) => {
setValue(value);
}}
renderInput={(params) => (
<Tooltip title={"select " + props.label + " date"} arrow>
<TextField size="small" {...params} />
</Tooltip>
)}
/>
);
}
props.date is an App-level state that is handed down to the picker component. checkDate is a simple function to ensure the date is in the correct range (I've bug-tested it, and it's not the cause of the problem as far as I can see). props.handler changes the App-level state. I wonder if the problem is that I'm manipulating state at the App-level directly through the picker.

This is now resolved!
The problem here is that props.date is a date object stored as an App state that is passed to the component. The component is modifying this directly! Instead it should work with a deep copy of the date object.
Instead of
value={props.date}
it should have
value={new Date(props.date.getTime())}

Related

Clearing a Material UI search filter TextField and returning to default

I am new to ReactJS and pairing it with Material UI is really causing me some roadblocks. I have created a reusable search filter component for my data tables and it worked exactly the way I wanted, but now I want to add a button to clear the field and show the unfiltered results, as well as return the InputSearch component back to its default state so it will display the label inside the field again, not up in the field’s border as these Material UI TextFields do then they are focused or have a current value. This is where I am hitting my roadblock. I have tried multiple solutions I found online, like using the inputRef/useCallback method to change the values, but it didn’t seem to work…or maybe I misunderstood and did it wrong. I was also recommended to put my search values to state. As happens with state my searches are now always one render behind (I.E. , results matching ‘US’ for ‘USA’ , ‘USA’ for ‘USAF’, etc…). Then when I run the handleFilterReset function to set the filter values back to an empty string, nothing happens. I just want my search filter to work instantly (like it did before I moved the value to state [commented out]) and be able to be cleared, resetting the table back to its default display.
Can someone please help me figure this out? Suggestions are appreciated, but code snippets are much more helpful since I am really new to React and especially Material UI.
dataTable.js
const [inputValue, setInputValue] = useState('')
const [searchFn, setSearchFn,] = useState({ fn: items => { return items; } });
// Searching Data
const handleSearch = e => {
setInputValue(e.target.value) // value displayed in input field
let query = (e.target.value).toString().toLowerCase();
setSearchFn({
fn: items => {
if (query === "")
return items;
else
return items.filter(x =>
(x.tankName !== null && x.tankName.toLowerCase().includes(query)) ||
(x.dimensions !== null && x.dimensions.toLowerCase().includes(query))
)
}
})
}
// Clearing Filters
const handleFilterReset = () => {
setInputValue('');
setSearchFn({fn: items => {return items;}})
};
// Search and filter Inputs
<div>
<InputSearch
value={inputValue}
onChange={handleSearch}
/>
<Button
text="Reset"
onClick={handleFilterReset}
/>
</div>
InputSearch.js
export default function InputSearch(props) {
const { inputRef, name, value, error=null, onChange, ...other } = props;
return (
<TextField
label="Search..."
name={name}
value={value}
onChange={onChange}
{...other}
{...(error && {error:true, helperText:error})}
>
</TextField>
)
}
You need to pass the value to InputSearch
Heres an example:
https://codesandbox.io/s/morning-brook-durbvd?file=/demo.tsx
React has a pretty good introduction on its site.
https://reactjs.org/docs/components-and-props.html
The code has been updated with a solution to this issue. I created a display value for the input that I passed to state, which was set to a blank string when the reset is pressed as well as passing an unfiltered data set.

React performance issue with drag and drop

I'm struggling with react performance issue while adding an element to the box. For this, I use React Rnd library.
I have a map that renders items when there is a new item inside the array:
children.map((children, index) => (
<Box
key={children.id}
isPreview={false}
index={index}
slot={name}
{...children}
/>
)),
Box component is Rnd component from the library, and it is actually big one.
<Rnd
style={{
//2 lines off css
}}
minHeight={MIN_SIZE}
minWidth={MIN_SIZE}
enableResizing={isResizingEnabled}
disableDragging={condition}
size={size}
position={Position}
lockAspectRatio={isAspectRatioLocked}
onResizeStart={onResizeStart}
onDragStop={(e, newPosition) => {
onDragStop(newPosition)
}}
onResizeStop={(e, dir, ref, delta, newPosition) =>
onResizeStop(ref, newPosition)
}
resizeHandleComponent={createResizeHandles(isInCollision)}
dragGrid={grid}
resizeGrid={grid}
bounds="parent"
>
<StyledDiv
onClick={() => {
dispatch(actions.setEditMode({...properties}))
}}
isBeingCropped={isCroppingEnabled}
isPreview={isPreview}
isEditable={isEditable}
isInCollision={isInCollision}
isEditStartable={isEditStartable}
>
{children}
</StyledDiv>
</Rnd>
And the problem is when I add 4 elements to this box, it took sometimes 2-4 seconds...
Any idea how it could be solved?
Is there any simple solution to make it faster, or do I have to investigate each function/hook and optimize it with some useCallback, useMemo, or something?
Yes, you should start from avoiding unnecessary re-renders, then you can look into optimizing each function. As this is a drag and drop component, i believe most props will change often whiles you drag an element, as a result make sure that each re-render is important before actually re rendering the component.
Here are a few places to start from.
Pass only what is needed to the box
Instead of spreading the children to the Box, pass only what is need to it, this will avoid any unnecessary re renders in case there are props in there that the Box do not care about.
children.map((children, index) => (
<Box
key={children.id}
isPreview={false}
index={index}
slot={name}
/>
)),
Use Memoization to avoid re renders
For times when the objects haven't moved, you may want to avoid any re renders, as a result memoize the Components.
Use useCallback for functions
This will prevent the callback from being assigned everytime the parent is re rendered. The biggest culprit i see is the onClick handler which depends on the actions, as a result will render the box again and again when the action changes, even though the box doesn't depend on all the actions
const { setEditMode } = dispatch;
const handleDragStop = useCallback(
(e, newPosition) => {
onDragStop(newPosition);
},
[onDragStop]
);
const handleResizeStop = useCallback(
(e, dir, ref, delta, newPosition) => {
onResizeStop(ref, newPosition);
},
[onResizeStop]
);
const handleOnClick = useCallback(() => {
dispatch(setEditMode({ ...properties }));
}, [onResizeStop, setEditMode, properties]);
<Rnd
style={
{
//2 lines off css
}
}
minHeight={MIN_SIZE}
minWidth={MIN_SIZE}
enableResizing={isResizingEnabled}
disableDragging={condition}
size={size}
position={Position}
lockAspectRatio={isAspectRatioLocked}
onResizeStart={onResizeStart}
onDragStop={handleDragStop}
onResizeStop={handleResizeStop}
resizeHandleComponent={createResizeHandles(isInCollision)}
dragGrid={grid}
resizeGrid={grid}
bounds="parent"
>
<StyledDiv
onClick={handleClick}
isBeingCropped={isCroppingEnabled}
isPreview={isPreview}
isEditable={isEditable}
isInCollision={isInCollision}
isEditStartable={isEditStartable}
>
{children}
</StyledDiv>
</Rnd>;
Checkout this article I wrote here if you need any further explanation

Display date in a list conditionally

Currently I'm working on small React project where I'm using data provided by a flight information API. Everything is working according to plan so far. I can display all array items in a list. Now I'm trying to implement a feature that displays date on a separate row in the list. I only want to display a date one time for one or more items with the same date.
Down below in the condition I'm using the variable displayDate. Basically just to be able to turn the date on/off for the moment and to test some logic. What I'm trying to figure now is what logic I need to be able to evaluate the if statement to either true or false. There has to be some sort of comparison between the current date and the next date in the array.
Each object in the array has a property called scheduleDate formatted as "2020-07-06".
Any ideas how I can solve this?
<Flights>
{resolvedData &&
resolvedData.flights
.filter((item) => item.flightName === item.mainFlight)
.map((item, index) => {
if (displayDate) {
return (
<React.Fragment key={item.id}>
<Date date={item.scheduleDate} />
<Flight flight={item} />
</React.Fragment>
);
} else {
return <Flight key={item.id} flight={item} />;
}
})}
</Flights>
Check if the scheduleDate is different than the previous
const renderTable = () => {
let currentDate = null;
return data.map((item, index) => {
if (item.scheduleDate !== currentDate) {
currentDate = item.scheduleDate;
return (
<>
<Date/>
<Flight/>
</>
);
} else {
return <Flight/>;
}
});
};
<Flights>{renderTable()}</Flights>

Editing text input with static data on react native

When using text input using this.state it doesn't matter to give value a state variable and you'll able to edit the value, in my case here I am rendering a list and when I specify value of text input I can no longer edit that value because it's outside the state, I don't want to use placeholder to show the value, I need a way to have it as value but also be able to edit that value.
<TextInput
keyboardType='decimal-pad'
value={String(roomData.room_attached_dimensions.depth)}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
Also I don't understand why should I wrap my values with String, it shows nothing otherwise.
My list looks like so
const attachedRooms = this.state.rooms_attached.map((item, index) => (
<View key={index}>
...
</View>
))
The function does nothing special
handleRoomEdit = (value) => {
this.setState({ roomEdit: true, value: value })
}
Of course I have different inputs I cannot simply give them different names, the data is stored on asyncStorage and even if I edit the array it dont work unless I re mount the damn component
Okay there's you error. You are not handling your input's value with state. That's why it does not work. You can do something like this -
<TextInput
keyboardType='decimal-pad'
value={this.state.value || String(roomData.room_attached_dimensions.depth)}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
I think this should work. Now you are handling your input's value with state. Or you can use defaultValue to give initial value, but I think react discourages using defaultValue and Value both -
<TextInput
keyboardType='decimal-pad'
defaultValue={String(roomData.room_attached_dimensions.depth)}
value={this.state.value}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
EDIT:: Also you can use ES6 to make your code clean -
handleRoomEdit = (value) => {
this.setState({ roomEdit: true, value }) //use ES6 shorthand //
}

React Native Picker moves back to first item

This works as expected, where the picker stays on the selected item...
<Picker
selectedValue={this.state.person}
onValueChange={(itemValue) => this.setState({person: itemValue})}
style={styles.picker}>
{Object.keys(coWorkers)
.map((result, index) =>
<Picker.Item
label={`${coWorkers[result].name}(${coWorkers[result].likes})`}
value={coWorkers[result].name}
key={index}
/>
)
}
</Picker>
I want to get multiple key/values from the coWorkers object in this.setState, so I am trying this...
<Picker
selectedValue={this.state.person}
onValueChange={(itemValue) => this.setState({person: itemValue.name, likes: itemValue.likes})}
style={styles.picker}>
{Object.keys(coWorkers)
.map((result, index) =>
<Picker.Item
label={`${coWorkers[result].name} (${coWorkers[result].likes})`}
value={coWorkers[result]}
key={index}
/>
)
}
</Picker>
However, now the picker jumps back to the top (this.state is being correctly updated though).
The type of the prop value for the Picker should be either string or integer. It is not clear from the docs on the website but you can see it in the Picker source code comments here https://github.com/facebook/react-native/blob/master/Libraries/Components/Picker/Picker.js
It does a simple equal check of selectedValue and picker items value to translate it to the native PickerIOS understands. https://github.com/facebook/react-native/blob/master/Libraries/Components/Picker/PickerIOS.ios.js#L53
Although the contents are same the object this.state.selectedValue and matching coWorkers[result] are different objects
You can generate some unique ids for each item in the array and use that to lookup the object.
If someone else has this problem and the solutions above are not working, it might be because you don't have the selectedValue flag set to something:
// You can replace the null with an actual value in your array(check to see it's exact)
const [toLanguage, setToLanguage] = useState(null);
selectedValue={toLanguage}
Your itemValue.name is not matching your coWorkers[result]

Categories