在嵌套功能组件 React Native 中键入 TextInput 时键盘关闭
Keyboard dismisses while typing TextInput in nested functional component React Native
我有一个奇怪的问题,当 TextInput 被放置在子功能组件中时,键盘在键入时一直关闭。如果将 TextInput 直接放在 Parent Component 下,则不存在此问题。这是我的代码
const SignInScreenC = props => {
// define Hook states here
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isEmailEmpty,setIsEmailEmpty] = useState(false);
const [isEmailValid,setIsEmailValid] = useState(true);
const [isPasswordEmpty,setIsPasswordEmpty] = useState(false);
/**
* Called when Sign in is clicked.
* checks if the form is valid
*/
const _OnSignInClicked = () => {
if(_isFormValid()) {
//make api call
}
}
/* Checks if the form is valid
*/
const _isFormValid = () => {
//reset values
setIsEmailEmpty(false);
setIsEmailValid(true);
setIsPasswordEmpty(false);
let isValid = true;
if(email.trim() === "") {
setIsEmailEmpty(true);
isValid = false;
}
else if(!AppUtils.isEmailValid(email)) {
setIsEmailValid(false);
isValid = false;
}
else if(password.trim() === "") {
setIsPasswordEmpty(true);
isValid = false;
}
return isValid;
}
const SignInForm = () => {
return (
<View style={styles.formStyle}>
<TextInput
key="email"
label={Strings.hint_email}
value={email}
keyboardType="email-address"
onChangeText={(text)=> {
setEmail(text)
setIsEmailEmpty(false)
setIsEmailValid(true)
}}
style={styles.marginStyle}
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<TextInput
key="pass"
value={password}
secureTextEntry ={true}
label={Strings.hint_password}
style={[styles.marginStyle,styles.stylePassword]}
onChangeText={(text)=> {
setPassword(text)
setIsPasswordEmpty(false)}
}
theme="light"
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<Button
style={styles.loginStyle}
title = {Strings.login}
onPressButton = {() => _OnSignInClicked()}/>
</View>
);
}
return (
<>
<ImageBackground source={Images.screen_backgound} style={{width: '100%',
height: '100%'}}>
<View style = {styles.viewOverlaystyle} />
<ScrollView contentContainerStyle = {{flexGrow:1}}
keyboardShouldPersistTaps={'handled'}>
<View style={styles.containerStyle}>
<SignInForm/>
</View>
</ScrollView>
</ImageBackground>
</>
);
}
const styles = StyleSheet.create({
....
})
const mapStateToProps = state => ({
userData : state.userData
});
const mapDispatchToProps = dispatch =>
bindActionCreators(UserActions, dispatch);
const SignInScreen = connect(mapStateToProps,mapDispatchToProps) (SignInScreenC)
export {SignInScreen};
如果我将所有内容 直接粘贴到呈现方法,一切正常。
您的 SignInForm
函数(被视为 React 组件,因为它的大写形式和称为 JSX)在您的 SignInScreenC
组件中声明。这意味着每次渲染都会创建 React 组件的新 type。
SignInScreenC
第一次渲染:创建 SignInForm
组件,实例化并渲染它
SignInScreenC
第二次渲染:创建 另一个完全不同的 SignInForm
组件,再次实例化它,有效地卸载旧的 SignInForm
和渲染新 SignInForm
就位
- 由于卸载了旧输入,您失去了键盘焦点
这是由于 React 处理渲染的方式:每当它遇到不同类型的元素应该渲染以代替旧元素时,旧元素将被卸载。作为反应,每个新的 SignInForm
都与前一个不同,因为您不断创建新功能
解决方案:
- 在
SignInScreenC
之外创建单独的 SignInForm
组件,并将所有必要的数据作为道具传递
- 或者,使用
const renderSignInForm = () => return (...)
而不是 const SignInForm = () => return (...)
,并且在渲染时,像 {renderSignInForm()}
一样调用它而不是 <SignInForm/>
。这样它就不会被视为组件,也不会被卸载
我在尝试将文本更改传播到父组件 (React Native) 时遇到了一个略有不同但相关的问题。
如果您的组件冒泡 onChangeText 事件并触发重新渲染并随后失去对键盘的关注,您还可以考虑在用户完成输入文本并保持本地状态后传播您的更改事件 onEndEditing用于文本输入。
export function YourTextInputComponent(
{ initialValue, onChangeTextDone } :
{ initialValue: string, onChangeTextDone : (text: string) => void) }
): JSX.Element {
const [text, setText] = useState<string>(initialValue);
return (
<TextInput
value={text}
onChangeText={(txt) => {
setText(txt);
}}
onEndEditing={(event) => {
onChangeTextDone(text);
}}
/>
)
}
我有一个奇怪的问题,当 TextInput 被放置在子功能组件中时,键盘在键入时一直关闭。如果将 TextInput 直接放在 Parent Component 下,则不存在此问题。这是我的代码
const SignInScreenC = props => {
// define Hook states here
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isEmailEmpty,setIsEmailEmpty] = useState(false);
const [isEmailValid,setIsEmailValid] = useState(true);
const [isPasswordEmpty,setIsPasswordEmpty] = useState(false);
/**
* Called when Sign in is clicked.
* checks if the form is valid
*/
const _OnSignInClicked = () => {
if(_isFormValid()) {
//make api call
}
}
/* Checks if the form is valid
*/
const _isFormValid = () => {
//reset values
setIsEmailEmpty(false);
setIsEmailValid(true);
setIsPasswordEmpty(false);
let isValid = true;
if(email.trim() === "") {
setIsEmailEmpty(true);
isValid = false;
}
else if(!AppUtils.isEmailValid(email)) {
setIsEmailValid(false);
isValid = false;
}
else if(password.trim() === "") {
setIsPasswordEmpty(true);
isValid = false;
}
return isValid;
}
const SignInForm = () => {
return (
<View style={styles.formStyle}>
<TextInput
key="email"
label={Strings.hint_email}
value={email}
keyboardType="email-address"
onChangeText={(text)=> {
setEmail(text)
setIsEmailEmpty(false)
setIsEmailValid(true)
}}
style={styles.marginStyle}
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<TextInput
key="pass"
value={password}
secureTextEntry ={true}
label={Strings.hint_password}
style={[styles.marginStyle,styles.stylePassword]}
onChangeText={(text)=> {
setPassword(text)
setIsPasswordEmpty(false)}
}
theme="light"
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<Button
style={styles.loginStyle}
title = {Strings.login}
onPressButton = {() => _OnSignInClicked()}/>
</View>
);
}
return (
<>
<ImageBackground source={Images.screen_backgound} style={{width: '100%',
height: '100%'}}>
<View style = {styles.viewOverlaystyle} />
<ScrollView contentContainerStyle = {{flexGrow:1}}
keyboardShouldPersistTaps={'handled'}>
<View style={styles.containerStyle}>
<SignInForm/>
</View>
</ScrollView>
</ImageBackground>
</>
);
}
const styles = StyleSheet.create({
....
})
const mapStateToProps = state => ({
userData : state.userData
});
const mapDispatchToProps = dispatch =>
bindActionCreators(UserActions, dispatch);
const SignInScreen = connect(mapStateToProps,mapDispatchToProps) (SignInScreenC)
export {SignInScreen};
如果我将所有内容
您的 SignInForm
函数(被视为 React 组件,因为它的大写形式和称为 JSX)在您的 SignInScreenC
组件中声明。这意味着每次渲染都会创建 React 组件的新 type。
SignInScreenC
第一次渲染:创建SignInForm
组件,实例化并渲染它SignInScreenC
第二次渲染:创建 另一个完全不同的SignInForm
组件,再次实例化它,有效地卸载旧的SignInForm
和渲染新SignInForm
就位- 由于卸载了旧输入,您失去了键盘焦点
这是由于 React 处理渲染的方式:每当它遇到不同类型的元素应该渲染以代替旧元素时,旧元素将被卸载。作为反应,每个新的 SignInForm
都与前一个不同,因为您不断创建新功能
解决方案:
- 在
SignInScreenC
之外创建单独的SignInForm
组件,并将所有必要的数据作为道具传递 - 或者,使用
const renderSignInForm = () => return (...)
而不是const SignInForm = () => return (...)
,并且在渲染时,像{renderSignInForm()}
一样调用它而不是<SignInForm/>
。这样它就不会被视为组件,也不会被卸载
我在尝试将文本更改传播到父组件 (React Native) 时遇到了一个略有不同但相关的问题。
如果您的组件冒泡 onChangeText 事件并触发重新渲染并随后失去对键盘的关注,您还可以考虑在用户完成输入文本并保持本地状态后传播您的更改事件 onEndEditing用于文本输入。
export function YourTextInputComponent(
{ initialValue, onChangeTextDone } :
{ initialValue: string, onChangeTextDone : (text: string) => void) }
): JSX.Element {
const [text, setText] = useState<string>(initialValue);
return (
<TextInput
value={text}
onChangeText={(txt) => {
setText(txt);
}}
onEndEditing={(event) => {
onChangeTextDone(text);
}}
/>
)
}