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.
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.
Hello guys I just started learning React-Native and I have a question about state.
I was practicing this concept trying to make a button that shows how many times I've pressed it.
My plan was to make a variable called clicks which will increase by 1 each time I press it and set the clickState to clicks. This is my code.
export default function App() {
const [clickState, setClicks] = useState(0)
let clicks = 0
return (
<View style = {styles.container}>
<StatusBar style="auto" />
<TouchableOpacity style={styles.button} onPress={()=>{setClicks(++clicks); console.log(clicks)}}>
<Text>Clicks : {clickState}</Text>
</TouchableOpacity>
</View>
);
}
this is the console
But apparently something is wrong and my clicks value goes random between 1 and 2 each time I click it instead of increasing by 1.
So I was curious about what I was doing wrong and why the values don't increase as I expected. I would also be glad if you showed how you would implement it if there is a better way.
Thanks guys.
You only need to update clickState, no need of variable clicks.
Also it won't rerender if we increment state value directly, so we should increment state by taking its previous state value like shown below
export default function App() {
const [clickState, setClicks] = useState(0)
return (
<View style = {styles.container}>
<StatusBar style="auto" />
<TouchableOpacity style={styles.button} onPress={()=>setClicks(prevState => prevState + 1)}>
<Text>Clicks : {clickState}</Text>
</TouchableOpacity>
</View>
);
}
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.
im new in react-native and im doing a bloc note, the problem now is that i want to allow the user put images with the note, something like this:
my structure for this for now is this:
render() {
return (
<>
<View style = {this.styles.container}>
<TextInput style = {this.styles.TextInput_title} placeholder = "Title" multiline = {true} maxLength = {80} value = {this.state.title} onChangeText = {title => this.setState({title: title})}></TextInput>
<View style = {this.styles.textinput_container}>
<TextInput style = {this.styles.textinput} multiline = {true} onChangeText = {content => this.setState({content: content})}><Text>{this.state.content}{"\n"}</Text>
<Text>{this.state.img}</Text>
</TextInput>
<Button title = "Add image" onPress = {this.add_Image}></Button>
<Button title = "Save Changes" onPress = {this.save_Changes}></Button>
</View>
</View>
}
add_Image = () => {
this.setState({content: this.state.content + "\n\n\n"});
const img = <Image source = {require("../img/ny.png")} style = {{width: 100, height: 100}}></Image>
this.setState({img:img});
}
for now im just using a button to add the image to see if it works, but i get this:
as you can see, the pointer (the blue line) is still there and doesnt does a line break, maybe is the way that im using to do this, but like i said, im new and for now i dont know another way to do this, so if anyone can help me, really will appreciate :)
You could achieve this by some modifications to the style:
E.G:
<Text style={{ flex: 1, marginTop: '25%' }}>{this.state.img}</Text>
Where,
marginTop: Gona gives you the proportion of the space you want to leave on top.
flex: Set it on 1 it's going to put it on its on space.
This my code sandbox example:
https://codesandbox.io/s/react-hooks-counter-demo-kevxp?file=/src/index.js
My problem is:
The list will always rerendering on every state change inside the page so the scroll will always back to the top. I want to know why this happen, and how to prevent this behaviour even the state of the list have changes then keep the last scroll position of the list
Every time App renders, you are creating a brand new definition for the Example component. It may do the same thing as the old one, but it's a new component. So react compares the element from one render with the element of the next render and sees that they have different component types. Thus, thus it is forced to unmount the old one and mount the new one, just as it would if you changed something from a <div> to a <span>. The new one begins scrolled to 0.
The solution to this is to create Example only once, outside of App.
const Example = props => (
<List
className="List"
height={80}
itemCount={props.propsAbc.length}
itemSize={20}
width={300}
itemData={{
dataAbc: props.propsAbc
}}
>
{({ index, style, data }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
{data.dataAbc[index]}
</div>
)}
</List>
);
function App() {
const [count, setCount] = useState(0);
let [dataArray, setDataArray] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
return (
<div className="App">
<h1>Scroll down the blue box, then click the button</h1>
<h2>You clicked {count} times!</h2>
<button onClick={() => setCount(count - 1)}>Decrement</button>
<button onClick={() => setCount(count + 1)}>Increment</button>
<div
style={{ maxHeight: "80px", overflow: "äuto", background: "lightblue" }}
>
<Example propsAbc={dataArray} />
</div>
</div>
);
}
https://codesandbox.io/s/react-hooks-counter-demo-qcjgj
I don't think it's a react window problem.
A react component re-renders because there's a state change. In this case, the state change is caused by setCount (when you click the increment button), which re-renders the entire component including Example.
If Example is its own component, the scroll position won't get refreshed, because it no longer depends on the count state.
A working sample here:
https://codesandbox.io/s/react-hooks-counter-demo-hbek7?file=/src/index.js