I am using passing function as prop to a component and updating the state of textinput on change.
It is working fine on the web browser and alert the same input written but when I run it on my android device using expo client.
It alerts undefined instead of the actual input.
My piece of code is:
//Main Component
const [search, setsearch] = useState('');
const searchinput = (inp) => {
setsearch(inp.target.value);
alert(search);
};
<Nav Search={searchinput} inpValue={search} Click={() => searchapi()} />
//Nav Component
<TextInput
value={props.inpValue}
onChange={props.Search}
style={{height: 25, width: 100, borderColor: 'white', borderWidth: 0}}
/>
You can do something like this:
const Nav = (props) => {
return (
<TextInput
value={props.inpValue}
onChange={props.Search}
style={{height: 125, width: 100, borderColor: 'black', borderWidth: 1}}
/>
);
};
export default function App() {
const [search, setsearch] = React.useState('');
React.useEffect(() => {
alert(search);
}, [search]);
const searchinput = (inp) => {
setsearch(inp.nativeEvent.text);
};
return <Nav Search={searchinput} inpValue={search} />;
}
Here the current TextInput text value is retrieved inside the onChange using inp.nativeEvent.text.
https://reactnative.dev/docs/textinput.html#onchange
I've also added a useEffect hook that correctly tracks the current value of the search state. With the way you were accessing the search state after updating it, you can't be sure that you're actually referring to current value of the TextInput
Related
I am making a class for a custom TextInput, where the style will change when the field is selected, and will change back as soon as it is pressed out of. It looks as follows...
export function SoftSearchBar({
height=40,
width='100%',
fontSize=20,
fireOnChange={function(){console.log("No Change Function in place")}},
value=false,
placeholder="Placeholder",
type=null
}){
const [isActive, setActive] = useState(false)
const [style, setStyle] = useState({})
useEffect(() => {
console.log(isActive)
if (isActive){
setStyle(style => ({style: styles.softSearchActive, width: width}))
}
else{
setStyle(style => ({style: styles.softSearchInactive, width: width}))
}
}, [isActive])
return(
<View style={{height: height, flexDirection: 'row'}}>
<TextInput
value={value}
onPressIn={() => setActive(true)}
onPressOut={() => setActive(false)}
style={{...style.style, width: width, zIndex: 0, fontSize: fontSize}}
textContentType={type}
text
placeholder={placeholder}
placeholderTextColor={'black'}
autoCorrect={false}
onChangeText={text => {
fireOnChange(text)
}}
/>
</View>
)
}
Almost all of this works as expected, when the field is pressed, an outline appears indicating its selection, and the text changes color. However, onPressOut fires immediately after onPressIn, as the log will look like this as soon as I press the field
true
false
indicating that onPressOut fired, since it is the only way to setIsActive(false)
I saw some solutions recommending using onResponderRelease as opposed to onPressOut but then it just never unselects. Is there some syntax Im missing with onPressOut? This seems like a pretty simple and straightforward syntax so I am unsure
Main Issue with your code is onPressIn and onPressOut you need to change them to onFocus and onBlur
Here is a working example you can paste into this website
https://reactnative.dev/docs/textinput
You can set your default Input style and then when active you can enable the style you want.
outlineStyle: none to get rid of the default blue outline of the textinput when focused
Can also just remove handleFocus & handleBlur and move the function into the actual function calls to reduce the code further
import React from "react";
import { SafeAreaView, StyleSheet, TextInput } from "react-native";
const UselessTextInput = () => {
const [style, setStyle] = React.useState({borderWidth:2 , borderColor: 'red', outlineStyle: 'none'});
const [active, setActive] = React.useState(false)
const handleFocus = () => setActive(true)
const handleBlur = () => setActive(false)
return (
<SafeAreaView>
<TextInput
style={[styles.input, active && style]}
onFocus={handleFocus}
onBlur={handleBlur}
onChangeText={() => {}}
value={null}
placeholder="useless placeholder"
keyboardType="numeric"
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default UselessTextInput;
I was wondering why there's 1 missing character every time I type on text input using onChangeText and onChange function simultaneously.
Here's my code:
import React, {useState} from "react";
import {Text} from "galio-framework";
export default function UpcomingSurvey ({ navigation }) {
const [timer, setTimer] = useState('')
const [saving, setSaving] = useState(false);
const [edited, setEdited] = useState(false);
const [inputValue, setInputValue] = useState('')
const inputChanged = () => {
setSaving(true)
clearTimeout(timer)
const newTimer = setTimeout(() => {
console.log('inputValue: ', inputValue)
}, 2000)
setTimer(newTimer)
}
function renderUpcomingSurvey () {
<Block style={{ paddingHorizontal: theme.SIZES.BASE }}>
<>
{saving &&
<Block style={{flex: 1}}>
<Text muted size={11} style={{ textAlign: 'right'}}>Saving</Text>
</Block>
}
{(edited && !saving) &&
<Block style={{flex: 1}}>
<Text muted size={11} style={{textAlign: 'right', color: '#36d79a'}}>saved</Text>
</Block>
}
</>
{!isLoading ? (
<Input
editable={radioCustom}
onChangeText={setInputValue}
onChange={inputChanged}
value={inputValue}
right placeholder="Type your custom question here."
iconContent={<Block />}
/>) : <Text></Text>
}
<Text muted size={12}>Free text | Your employees will see that this is a custom question</Text>
</Block>
When I try to console.log('inputValue: ', inputValue) inside inputChanged function will give missing one character. Example when I try to input Text this will give value in console Tex which missing character t.
Maybe there's a delay on capturing text value input after the onChange function triggers
What's the real culprit of these code?
It is probably the async nature of the setState function
Try to log the input value like this:
useEffect(() => {
console.log(inputValue)
}, [inputValue]) //Only run when inputValue is set
Solve it by assigning the Input defaultValue={inputValue} instead of value={inputValue}
I'm using React Native and I get this error:
Render Error Invalid hook call. Hooks can only be called inside of the body of a function component...
The problem is, there seems to be nothing wrong with the hook I use (useState), the error says I have a problem at this line:
const [isOpen, setIsOpen] = useState<boolean>(false);
I read the Rules of Hooks but I don't see anything wrong with this.
This is my component:
const GoalCard = (goal: IGoal) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const btnsOpacity = useRef(new Animated.Value(0)).current;
const initialeCardHeight = goal.deadline != undefined ? 150 : 80
const cardHeight = useRef(new Animated.Value(initialeCardHeight)).current;
const toggleCard = () => {
setIsOpen(!isOpen);
resizeCard();
if(isOpen) {
fadeOutBtns();
} else {
fadeInBtns();
}
}
}
This is where I call toggleCard (in the same file):
const renderDeadline = () => {
return (
<View style={styles.footer}>
<View style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
<Icon name="time-outline" size={28} color="#A0A4BA" style={{marginRight: 4}}/>
<Text style={styles.deadlineText}>Deadline</Text>
<Text style={styles.dateText}>test</Text>
</View>
<TouchableOpacity onPress={() => toggleCard()}>
<Icon name={isOpen ? "chevron-up-outline" : "chevron-down-outline"} size={21} color="#A0A4BA"/>
</TouchableOpacity>
</View>
)
}
renderDeadline component is inside GoalCard, and I saw in the Rules of Hooks that this might be the cause for this error.
So I tried removing it, and using the JSX of renderDeadline directly in GoalCard instead, but I got the same error (tried deleting the app and npm run iOS again, not just hot reload).
I want to display 30 pages of text on a screen. I've tried ScrollView and FlatList but I get a white screen. Only when I try with ScrollView to display only 2 pages, works fine.
I do not want to use a WebView, because I would like to have all data in the app (no internet connection needed).
Here is what I've already tried:
With FlatList:
I have a text.js as a model, which I use to create a Text Object in an array, which I then use as data for the FlatList. For the renderItem function (of FlatList) I use a TextItem to display the text.
text.js
function Text(info) {
this.id = info.id;
this.text = info.text;
}
export default Text;
LongTextModule.js
import Text from '../../models/text';
export const LONGTEXT = [
new Text({
id:'text_1',
text:`.....longtext....`
})
]
TextItem.js
const TextItem = (props) => {
return (
<View style={styles.screen} >
<Text style={styles.textStyle}>{props.longText}</Text>
</View >
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
},
textStyle: {
justifyContent: 'flex-start',
alignItems: 'flex-start',
fontFamily: 'GFSNeohellenic-Regular',
fontSize: 20,
padding: 10,
}
});
TextDetailScreen.js
const TextDetailScreen = (props) => {
const renderText = data => {
return <TextItem longText={data.item.text} />
}
return <FlatList
data={LONGTEXT}
keyExtractor={(item, index) => item.id}
renderItem={renderText}
/>
};
I think it's needless to show the code with ScrollView, since ScrollView is only for a small list.
I even tried to render the longText like this in the screen.
Without the ScrollView I get the first portion, but with ScrollView a white screen.
const TextDetailScreen = (props) => {
return (
<ScrollView>
<Text> ...longText...</Text>
</ScrollView>
);
};
I'm sure there is a way to display a lot of pages of text on a screen?
But how?
Thank you :)
It seems not to be an unknown Issue, I've also read from time to time about this issue.
But not to use Webview, because you wan't to have all Data in your app - don't have to be an Argument against Webview. With WebView, you also can display Data from your App-Storage.
Example:
<WebView style={styles.myStyle} source={{html: `<p style="font-size:48px">${longtext}</p>`}} />
Is it possible that for all the rows of Flatlist there is one static state.
I have a Component which i want to display only on the Flatlist's one row when i click on it. And when i click on other row of Flatlist The Component should not render on previous row but on current clicked row.
Here I have two Rows of Flatlist. I want To render Progress bar only for the clicked cardview or Play Buton.
So my logic is when i click the Button Then I somehow put the keyID of clicked Flatlist's rows in the global state which will only render the clicked Card View's progress bar.
Somehow Like this code:
{ GlobalState===this.props.key && (<ProgressBar />)}
as a checker for each Row render in flatlist
I ended up using global variable in component and prop of component.
In renderItem of Flatlist:
function in renderItem of Flatlist
_play = () => {
this.setState({ playState: "playing" });
TrackPlayer.play();
const thisplaying = this.props.item.Key;
ProgressBar.setCurrentPlayer(thisplaying);//Here I call to put the Key of Item to the component's global variable
};
using component in render of renderItem of Flatlist
<ProgressBar PlayerKey={this.props.item.Key} />
In Component{ ProgressBar.js } used in renderItem of Flatlist:
import React from "react";
import { View, Slider, Text} from "react-native";
import TrackPlayer, { ProgressComponent } from "react-native-track-player";
import { formatTime } from "./utils";
global.MineKey = null; //Here I used Global Variable
class ProgressBar extends ProgressComponent {
static setCurrentPlayer = player => {
global.MineKey = player; //Setting global variable on every call from the cardview upon play
};
onSliderEditStart = () => {
TrackPlayer.pause();
};
onSliderEditEnd = () => {
TrackPlayer.play();
};
onSliderEditing = value => {
TrackPlayer.seekTo(value);
};
render() {
const position = formatTime(Math.floor(this.state.position));
const duration = formatTime(Math.floor(this.state.duration));
return global.MineKey == this.props.PlayerKey ? ( //If Global variable key is equal to the Key of RowItem of Flatlist
<View style={{ flexDirection: "row",alignItems: "center", flex: 1,width: "100%"}}>
<Text style={{ color: "white", alignSelf: "center" }}>{position}</Text>
<Slider
onTouchStart={this.onSliderEditStart}
onTouchEnd={this.onSliderEditEnd}
onValueChange={this.onSliderEditing}
value={this.state.position}
maximumValue={this.state.duration}
maximumTrackTintColor="gray"
minimumTrackTintColor="white"
thumbTintColor="white"
style={{
flex: 1,
alignSelf: "center"
}}
/>
<Text style={{ color: "white", alignSelf: "center" }}>{duration}</Text>
</View>
) : null;
}
}
module.exports = ProgressBar;