TextInput /Input element looses focus on key press - reactjs

I'm currently developing an application using React Native.
This trial app has a component that has a TextInput and two buttons (ADD and DELETE).
When I press the ADD Button, a new component appears. If I press the DELETE Button that the same component disappears.
The screen is like the photo bellow:
I control the TextInput with the index which is the same number as the index of the component.
My question is: why can't I enter some text as usual in this code?
I have to focus the cursor every time I enter 1 word.
I lose a flashing vertical bar(I check in the photo below) in the input area every time I press a key.
How can I resolve this problem?
And, I want to control the inputted value from TextInput with array[] not object{} because in case of an array is easier to delete a component sliding index and value like an explanation below:
I have no idea to control index and value with an, object and it's complicated for my skill now, but if there are some nice ways to resolve using object, I hope to know it.
Here is the code:
import React, { useState } from "react";
import { View, Text, Button, TextInput, StyleSheet } from "react-native";
function Item({ number, handleInput, handleAdd, handleDelete, index }) {
return (
<View style={styles.list}>
<Text>{index}</Text>
<TextInput
style={{ borderWidth: 1 }}
value={number[index]}
onChange={(e) => {
handleInput(index, e.nativeEvent.text);
}}
></TextInput>
<Button
title="ADD"
onPress={() => {
handleAdd();
}}
/>
<Button
title="DELETE"
onPress={() => {
handleDelete(index);
}}
/>
</View>
);
}
export default function TestStateArray() {
const [count, setCount] = useState(1);
const [number, setNumber] = useState([]);
function handleAdd() {
setCount((v) => v + 1);
}
function handleDelete(index) {
setCount((v) => v - 1);
setNumber((v) => {
const ret = v.slice();
ret.splice(index, 1);
return ret;
});
}
function handleInput(index, text) {
setNumber((v) => {
const ret = v.slice();
ret[index] = text;
return ret;
});
}
return (
<View>
{Array.from({ length: count }, (_, i) => (
<Item
number={number}
handleInput={handleInput}
handleAdd={handleAdd}
handleDelete={handleDelete}
key={i + "-" + number}
index={i}
/>
))}
</View>
);
}
const styles = StyleSheet.create({
list: {
margin: 10,
padding: 10,
backgroundColor: "#ddd",
},
});
After I have some answer and some comment, I tried changing the code like bellow, but it still has the same problem...
// onChange={(e) => {
// handleInput(index, e.nativeEvent.text);
onChangeText={(text) => {
handleInput(index, text);
}}
node : 12.18.3
react native : 4.10.1
expo : 3.22.3

The above issue occurred because your handle change function is wrong.
Please change...
const [number, setNumber] = useState({}); // change array to Object in useState.
Replace your handler with below function:
function handleInput(index, text) {
setNumber({ ...number, index: text });
}

React Native has onChangeText event on TextInput component can you try that one?

Related

Passing a button select choice back to previous screen

So after some research, I have learned how to make a button that will take the user to another screen, and provide them a text input where they can enter some words, then on pushing the done button take them back to the previous screen where what they typed will be displayed. But for my particular needs, I am trying to figure out how to instead of a text input have a selection of buttons, such as "large, medium, small" and have that button select the data that would be displayed instead, and return them to the previous page where it is displayed.
initial screen
function HomeScreen( route ) {
navigation = useNavigation();
React.useEffect(() => {
if (route.params?.post) {
}
}, [route.params?.post]);
return (
<View>
<Pressable
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</Pressable
</View>
);
}
button selection screen
function CreatePostScreen( route ) {
const navigation = useNavigation();
const [postText, setPostText] = React.useState('');
return (
<>
<Pressable
title="Done"
onPress={() => {
navigation.navigate({
name: 'Home',
params: { postText },
merge: true,
});
}}
>
<Text>
Large
</Text>
</Pressable>
</>
);
}
any insight is greatly appreciated.
You can pass the data in form of object
{post: postText,
buttonType: 'medium'}
For getting the data
React.useEffect(() => {
if (route.params?.post) {
var buttonType= route.params?.buttonType
}
}, [route.params?.post]);
You can store the button type in a variable or state
var buttonType = route.params?.buttonType
Or You can try with useState() hooks
const [buttonType, setButtonType]=useState("")
setButtonType(route.params?.buttonType)
The for using it just do the following
<Text>{buttonType}</Text>
Please follow the React-Documentation

Scroll down to a specific View in React Native ScrollView

I want to be able to scroll a after pressing button so that it visible on screen. How can I tell a react-native ScrollView move to a certain?
Hello you can use the property scrollTo like below
import {useRef} from "react"
import {
ScrollView,
Button,
} from 'react-native';
const YouComp = () => {
const refScrollView = useRef(null);
const moveTo = () => {
refScrollView.current.scrollTo({x: value1, y: value2});
// or just refScrollView.current.scrollTo({x: value1}); if you want to scroll horizontally
// or just refScrollView.current.scrollTo({y: value2}); if you want to scroll vertically
}
return (<>
<Button onPress={moveTo} title="title" />
<ScrollView ref={refScrollView}>
...
</ScrollView>
</>);
}
You can set whether x or y value or both
Check the full doc here
First you need to create reference to element
this.scrollViewRefName = React.createRef()
Then pass it to ref attribute
<ScrollView ref={this.scrollViewRefName}>
Then you trigger the function from your button with scrollToTheEnd or wherever you want to scroll within the element
<View style={styles.ButtonContainer}>
<Button onPress={() => { this.scrollViewRef.current.scrollToTheEnd }} />
</View>
Note that you may need extra callback function in onPress depending on from which context you have the components
using ref and scrollTo is just bullshit and dose not always work.
Here is how i did it.
const [scrollYPosition, setScrollYPosition] = useState(0);
const [data, setData] = useState([]);
const goToItem = () => {
// lets go to item 200
// the 200 is the item position and the 150 is the item height.
setScrollYPosition(200 * 150);
}
<ScrollView contentOffset = {
{
y: scrollYPosition,
x: 0
}
}>
// papulate your data and lets say that each item has 150 in height
</ScrollView>

useEffect cleanup when triggered by state

I have a component that I trigger via a state object:
const Alerts = () => {
const alert = useStore((state) => state.alert);
const setAlert = useStore((state) => state.setAlert);
const alertsModalRef = useRef(null);
useEffect(() => {
console.log("TRIGGER");
if (alert) {
alertsModalRef.current?.present();
}
}, [alert]);
...
I trigger it with a button click from another component on different screens. For example:
export function UpdateSomething() {
const setAlert = useStore((state) => state.setAlert);
return (
<View>
<Button
title="Continue"
onPress={() => {
setAlert("something");
}}
/>
</View>
);
}
When I load my app and hit a triggering button, I see "TRIGGER" is printed once to the console. However, when I go to another screen and hit another button or even when I then go back to the first screen and hit the same button again, it then prints "TRIGGER" three times.
I suspect that I need to add some kind of cleanup method to the useEffect - but if so, what is the cleanup required there?
And if it's not that? what is going on?
Sorry, I realize this may have been asked in various forms a million times before. I read the docs and many posts, but still confused.
Update 1
As Drew recommended, I tested and saw the Alerts component is re-mounted whenever I visit a screen in my app, which is using React Navigation.
I also wrap each screen with the following Provider:
Navigator
const WrappedSearchScreen = withModalProvider(SearchScreen);
function SearchNavigator() {
const SearchStack = createStackNavigator();
return (
<SearchStack.Navigator>
<SearchStack.Screen
name="Search"
component={WrappedSearchScreen}
/>
...
</SearchStack.Navigator>
);
}
Provider
...
import { Alerts } from "./Alerts";
import { useStore } from "./store";
export const withModalProvider = (Component: FC) => () => {
const progressBarVisible = useStore((state) => state.progressBarVisible);
return (
<BottomSheetModalProvider>
<Component />
<Alerts />
<ProgressBar
indeterminate
color="green"
visible={progressBarVisible}
style={{ position: "absolute", bottom: 0, left: 0 }}
/>
</BottomSheetModalProvider>
);
};

How to delete item from an array by onPress event in react

I'am making a todo app. I want to call a function on a button Press event and want to pass item id to it.
import React ,{useState} from 'react';
import { StyleSheet, Text, View, TextInput, Button, ScrollView, Alert, FlatList} from 'react-native';
export default function App() {
const [goal,setgoal] = useState('');
const [addInput, setInput] = useState([]);
const changetext= ()=>{
setInput(addInput=>[...addInput,{id: Math.random().toString(), value: goal}]);
};
const texthandler= (enteredText)=>{
setgoal(enteredText);
};
const deleteText = (e)=>{
setInput((addInput)=>addInput.filter(todo=>todo.id !=e.target.id))
}
return (
<View style={styles.screen} >
<View style={styles.InputView}>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}
style={styles.TextInputStyle}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View style={styles.recordList}>
<Text style={styles.Textoutput} id={itemData.item.id} onPress={deleteText}>{itemData.item.value}</Text>
<Button title="Delete" style={styles.DeleteButton} value={itemData} onPress={deleteText} ></Button>
</View>)}/>
</View>
);
}
here i want Delete button to remove respective element from 'addInput' list. same thing is happening by pressing Text field itself.
but here i can pass id from text field but not from button. why so?
how to get it done by using button.
also, should i use 'this' keyword? can we do it without it, because some time it looks confusing to me at initial stage.
thanks in advance
Try this:
const deleteText = (itemID)=>{
setInput(()=>addInput.filter(todo=>todo.id !=itemID))
}
return (
<View style={{marginTop:30}}>
<View>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View>
<Text id={itemData.item.id} onPress={()=>deleteText(itemData.item.id)} >{itemData.item.value}</Text>
<Button title="Delete" value={itemData} onPress={()=>deleteText(itemData.item.id)} ></Button>
</View>)}/>
</View>
Well i am not sure about the answer in terms of React-native but in React i would try something like this:-
onPress= { this.deleteText.bind(this,idx) }
If you are not passing this function as a prop, simply remove bind and call it using this.deleteText(idx)
In react/react-native, you can make a event handler method and can handle array items for example like this:
handleOnPress = value => {
let {mArray} = this.state
if(mArray.includes(value)){
let index = mArray.findIndex((item => item==value))
mArray.splice(index, 1)
}else mArray.push(value)
this.setState({mArray})
}
In Your case, have to just delete item from the array so you can just use splice method. for example:
handleOnDelete = item => {
let {mArray} = this.state;
let index = mArray.findIndex((item => item==value))
if(index > -1) mArray.splice(index, 1)
else console.log('item not found')
}
const deleteText = e => setInput(
addInput => addInput.split(addInput.findIndex(todo => todo.id == e.target.id), 1)
)

React component won't take onChange event

Obligatory "new to react" paragraph here. I have this rating component I got from material-ui and i'm trying to send the value to a database.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Rating from '#material-ui/lab/Rating';
import Box from '#material-ui/core/Box';
const labels = {
0.5: 'Worst of the Worst',
1: 'Bad',
1.5: 'Poor',
2: 'Passable',
2.5: 'Ok',
3: 'Good',
3.5: 'Damn Good',
4: 'Great',
4.5: 'Love',
5: 'Perfection',
};
const useStyles = makeStyles({
root: {
width: 200,
display: 'flex',
alignItems: 'center',
},
});
export default function HoverRating(props) {
const [value, setValue] = React.useState(2);
const [hover, setHover] = React.useState(-1);
const classes = useStyles();
const onRatingChange = (event) => {
console.log(event.target.value)
props.reduxDispatch ({ type: "RATING_CHANGE", value: event.target.value
})
}
return (
<div className={classes.root}>
<Rating
name="hover-feedback"
value={value}
defaultValue={0}
precision={0.5}
size="large"
onChange={(event, newValue) => {
setValue(newValue);
console.log("your newValue is " + newValue)
}}
onChangeActive={(event, newHover) => {
setHover(newHover);
}}
{ onRatingChange }
/>
<br/>
{value !== null && <Box ml={2}>{labels[hover !== -1 ? hover : value]}</Box>}
</div>
);
}
It doesn't like something about my onRatingChange function. I've moved it all over the place and it's still throwing errors. I just really don't understand the issue. I'm mostly getting-
"./src/components/Rating.js
Line 54:11: Parsing error: Unexpected token, expected "..."
I've been at this for hours and I salvation.
Change your code from:
{ onRatingChange }
to:
onRatingChange={onRatingChange}
and change your file extension from .js to .jsx because you are using the JSX syntax
First, you appear the be storing the rating in two places: in your local state (with React.useState), and from the looks of your onRatingChange function, in a Redux store somewhere. It would be a good idea to pick one, and use that.
As for the direct answer to your question, your syntax is wrong. You're writing your Rating component in the following way:
<Rating
// ...
onChange={(event, newValue) => {
setValue(newValue);
console.log("your newValue is " + newValue)
}}
// ...
{ onRatingChange }
/>
The Rating component expects an onChange prop. I assume you want your onRatingChange function to be called when the rating changes. As such, you'd write:
<Rating
// ...
onChange={onRatingChange}
/>
The complication here though is that you're trying to register two different handlers for the rating change event. The bottom line is, decide on one, and then pass that as a callback function to the onChange prop.

Resources