I have been trying to add Slider component to a react project. functionality wise its working fine but i am having two issues which i am not able to get rid of
changing value of the slider is not smooth. Dragging doesn't work properly, its just drags to the nearest value and then stops.
On a mobile device its even worse, no dragging at all, i have to tap on the exact spot for the slider to move.
I did find the issue, i was using onChange, so when i removed it it worked exactly like the example. But i need to update state of parent component, so added line 18, but then again the same issue appeared. I fi remove line 18 then all this gets fixed but i need line 18 to call a function of parent component, to update its state variable.
Here is the gist link of my code
https://gist.github.com/kapiljhajhria/0e9beda641d561ef4448abf9195dbcca
import React from "react";
import Slider from "#material-ui/core/Slider";
export default function SliderWithLabel(props) {
const {
labelText, range = {
min: 0,
max: 10
}, step = 1,
// defaultValue = Math.ceil((range.min + range.max) / 2),
handleSliderChange,
name,
value: sliderValue
} = props;
function sliderValuetext(value) {
// handleChange({target: {value: value}});
if(value!==sliderValue)handleSliderChange(value,name)
return `${value}`;
}
return (
<div className="sliderField" style={{display: "flex", flexDirection: "column"}}>
<div>
{labelText}
</div>
<Slider
style={{width: "90%", justifyContent: "center", display: "flex", margin: "auto"}}
defaultValue={sliderValue}
getAriaValueText={sliderValuetext}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
// onChange={sliderChange}
step={step}
// name={name}
// onChange={handleChange}
marks
min={range.min}
max={range.max}
/>
</div>
)
}
after spending 2 days on the issue, creating a sample project , trying to recreate the issue , it turned out to be a simple fix.
Parent component has a FORM, key which i was using for the form was
Date().getTime()
This was what was causing the issue with the slider. My guess would be that it was rebuilding the whole form with each slider value change. Which made slider UI behave in such a way. using appropraite key fixed the issue. I am now switching between two key value.
Related
I'm trying to get the height of a div and show a "scroll back to top" button when a certain amount of pixels are passed. But I can only seem to get height of said div and either only show or hide the button. Currently it says the height is 480. Here is my code:
const [topButton, setTopButton] = useState(false)
const vendorCard = document.getElementById('vendorCard')?.offsetHeight!
useEffect(() => {
if(vendorCard > 480){
setTopButton(true)
} else {
setTopButton(false)
}
}, [])
// Need to hide button until scrolled
const scrollToTop = () => {
document.getElementById('vendorCard')?.scrollTo({
top: 0,
behavior: 'smooth'
})!
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js">
{topButton && <div>
<button
onClick={scrollToTop}
className="hidden"
id="resetScroll"
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '10px',
border: 'none !important'
}}
type="button"
>
<ArrowUpwardIcon />
<p style={{ fontSize: '0.7em', fontWeight: '200' }}>BACK</p>
</button>
</div>}
</script>
If I am understanding correctly, you are using useEffect and telling it to only check that if statement once at load by giving it an empty array as a second argument.
You need an onScroll listener on vendorcard. Im not sure what vendorcard is as its not in your code.
assuming this is vendor card...
<div id='vendorcard' onScroll = {handleScroll}></div>
then you want to make a function...
function handleScroll(event){console.log(event.currentTarget)}
// event.currentTarget exposes the properties you need to access to build your if statement that alters your state.
You shouldnt need useEffect at all in that function as it doesnt really have a dependancy. You basically call that function onScroll, then inside that function you write your if statements to set the state of topButton. However, it is possible, depending on your end goal and code setup, that useEffect may be needed somewhere.
You could also write the function anonymously for onScroll if it is short. However, its almost always better for readability if you use a seperate function.
Also, avoid interacting with the DOM directly.
QuerySelector,getElementById etc.... should only be used in very rare circumstances inside of React. There is almost always another way.
i'm usign below Mui Slider component
for a UI where i have to restrict it's value for certaing range. For example slider's ball will not dragable after 50. user can select values upto 50 but it will show it full range. i didn't found any direct solution so i figured out a bypass with Discrete Slider Values. here's the code sandbox link ,Where i have make a array of full available values. Is there any neat and clean solution ?
<Slider
size="small"
defaultValue={30}
aria-label="Small"
valueLabelDisplay="auto"
/>
Hello Fazlay Rabbi it's so simple you need just think out of the box, just need to write simply if condition I write your solution in blow :
import React, { useState } from "react";
import Box from '#mui/material/Box';
import Slider from '#mui/material/Slider';
export default function DiscreteSliderLabel() {
const [sliderValue, setSliderValue] = useState(0);
const handleChange2 = (event, newValue) => {
if (newValue > 50){
setSliderValue(50);
}else{
setSliderValue(newValue);
}
};
return (
<Box sx={{ width: 300 }}>
<Slider
value={sliderValue}
aria-label="Always visible"
onChange={handleChange2}
valueLabelDisplay="on"
/>
</Box>
);
}
I think you have to determine max value for yor slider like below
<Slider
...
max={50}
min={0}
/>
And i see your code and i think you can determine marks array for just labels not all values
I'm working on a react functional components project, wherein I've to increment scroll position programmatically when a user actually scrolls in a div.
for example, I've a div with id testDiv & a user scrolled down a bit, now the scroll position is 100, so I want to programmatically increment it by 1 and make it 101.
Problem statement: The scroll position keeps on incrementing via onScroll handler, so the scrollbar only stops at the end of the element even if we scroll only once.
Expected behaviour: Scroll position should be incremented only once by the onScroll handler if we scroll once on the UI.
What I tried: (dummy code for the reproduction purpose)
import React, { useCallback } from "react";
import ReactDOM from "react-dom";
const App = (props) => {
const onScroll = useCallback((event) => {
const section = document.querySelector('#testDiv');
// **The problem is here, scrollTop keeps on incrementing even if we scrolled only once**
if (section) section.scrollTop = event.scrollTop + 1;
}, []);
return (
<div id="testDiv" onScroll={onScroll} style={{ height: "500px", overflowY: "scroll" }}>
<div>test</div>
<div className="forceOverflow" style={{height: 500 * 25}}></div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
What you're looking for is throttling or debouncing the function. It keeps on incrementing because with every bit of scroll, onScroll is being called.
There are many ways to throttle functions in react but I like to use debounce from lodash. If I remember correctly it was about 1.5kb gzipped.
I made you a sandbox. And here is the code:
import React, { useCallback } from "react";
import _debounce from "lodash/debounce";
export const App = (props) => {
let debouncedOnScroll = useCallback(
_debounce(() => {
// YOUR CODE HERE
console.log("CHECK THE CONSOLE TO SEE HOW MANY TIMES IT'S GETTING CALLED");
}, 150), []); // Wait 150ms to see if I'm being called again, if I got called before 150ms, don't run me!
return (
<div
id="testDiv"
onScroll={debouncedOnScroll}
style={{ height: "500px", overflowY: "scroll" }}
>
<div>test</div>
<div className="forceOverflow" style={{ height: 500 * 25 }}></div>
</div>
);
};
By the way, use useRef API instead of document.querySelector. This query selector is getting called with every scroll and it's not the lightest weight on the client computer.
In my react native app, contains multiple TextInputs for a form which are rendered like this:
{this.props.steps.map(step, index) => (
<TextInput
multiline={true}
value={this.props.steps[index]}
placeholder="Enter Step"
onChangeText={value => this.handleFieldChange(value, index)}
style={{ padding: 10, fontSize: 15 }}
/>
)}
In the onChangeText function, the value of the textinput is edited using redux and the form is validated as so:
handleFieldChange = async (value, index) => {
var steps = this.props.steps;
steps[index]= value;
store.dispatch(updateSteps({ steps: steps }));
this.validateForm();
};
This means the TextInput's value doesn't get updated immediately so when the user types relatively fast, the it flickers.
Can someone suggest how to make the Text Input get updated more smoothly?
After testing for a while, I realised that it was nothing to do with the onChangeText function. I found that the TextInput only flickered after its contents exceeded the initial width of the component. Therefore making the TextInput the full width of the screen and adding textAlign to center the input solved the issue.
var width = Dimensions.get("window").width
<TextInput
multiline={true}
value={this.props.steps[index]}
placeholder="Enter Step"
onChangeText={value => this.handleFieldChange(value, index)}
style={{ width: width, padding: 10, fontSize: 15, textAlign: "center" }}
/>
This issue didn't occur if the TextInput was the only component in the screen, but only when it was nested in several Views as was the case here. However, I have no idea what is the direct cause of this error.
In the rendering step could be used (irrelevant, I know) and a key would be used, I would change this
value={this.props.steps[index]}
in
value={step}
key={index}
As already commented, in handleFieldChange you are changing props in a bad way, this:
var steps = this.props.steps;
needs to be changed in:
var steps = [...this.props.steps];
More than this I see no evidence why handleFieldChange needs to be an async function, I would remove async from its declaration.
Last, the root source of the problem could be in updateSteps or in validateForm...
Hope this helps.
I am trying to get familiar with React and the fixed-data-table-2 package by following the tutorial of https://github.com/schrodinger/fixed-data-table-2/blob/master/examples/ResizeExample.js.
I am encountering an odd error in that when I resize a column such that a horizontal scroll bar is needed, the scroll bar does not resize proportional to the table width. Rather it allows the user to scroll past all the column-headers.
The series of steps to recreate the error are:
Hit the Page
Resize Company Column and view the scrollbar is visible
Start Scrolling
Continue scrolling past all my columns
I was wondering if I had missed a configuration of the Table Component?
"use strict";
import React from 'react';
import FixedDataTable from 'fixed-data-table-2';
const {Table, Column, Cell} = FixedDataTable;
class BackupTable extends React.Component {
constructor(props) {
super(props);
this.state = {
dataList: [],
columnWidths: {
firstName: 240,
lastName: 150,
sentence: 140,
companyName: 60,
},
};
this._onColumnResizeEndCallback = this._onColumnResizeEndCallback.bind(this);
}
_onColumnResizeEndCallback(newColumnWidth, columnKey) {
console.log("SETTING NEW WIDTH (" + newColumnWidth+") of " + columnKey);
this.setState(({columnWidths}) => ({
columnWidths: {
...columnWidths,
[columnKey]: newColumnWidth,
}
}));
}
render() {
var {dataList, columnWidths} = this.state;
return (
<Table
rowHeight={30}
headerHeight={50}
rowsCount={10}
onColumnResizeEndCallback={this._onColumnResizeEndCallback}
isColumnResizing={false}
touchScrollEnabled={true}
width={1000}
height={500}
{...this.props}>
<Column
columnKey="firstName"
header={<Cell>First Name</Cell>}
cell={<Cell>Basic content</Cell>}
fixed={true}
width={columnWidths.firstName}
isResizable={true}
/>
<Column
columnKey="lastName"
header={<Cell>Last Name (min/max constrained)</Cell>}
cell={<Cell>Basic content 2</Cell>}
width={columnWidths.lastName}
isResizable={true}
minWidth={70}
maxWidth={170}
/>
<Column
columnKey="companyName"
header={<Cell>Company</Cell>}
cell={<Cell>Basic content 4</Cell>}
width={columnWidths.companyName}
isResizable={true}
/>
<Column
columnKey="sentence"
header={<Cell>Sentence</Cell>}
cell={<Cell>Basic content 3</Cell>}
width={columnWidths.sentence}
isResizable={true}
/>
</Table>
);
}
}
module.exports = BackupTable;
Screen Shots:
INTIAL_TABLE
START_SCROLLING
Running the example you provided in this sandbox - it seems like your table is behaving as expected (no errors).
As soon as the sum of the column widths exceeds the width you set for the table (1000px) - the table starts to overflow horizontally and the horizontal scrollbar appears.
In case the sum of the column widths is less than 1000px (like in the initial state) - a 5th "remainder" column appears in order to "fill up" the table to the table width you set.
I think you will see it more clearly if you try playing around with a smaller table width (try changing it to 500 in the sandbox I shared).
In case you are not interested in the horizontal scroll perhaps you should consider setting the overflowX="hidden" prop on your table.