使用自定义组件(组合模式)时,外层对原始组件的操作
CustomInput.tsx
import React, { useState, forwardRef } from 'react';
import {
StyleSheet,
View,
Image,
Text,
TextInput,
LayoutAnimation,
TouchableOpacity,
} from 'react-native';
import icon_error from '../assets/images/icon_error.png';
import icon_right from '../assets/images/icon_right.png';
import icon_question from '../assets/images/icon_question.webp';
import icon_delete from '../assets/images/icon_delete.png';
export default forwardRef<any, TextInput>((props, ref) => {
const [value, setValue] = useState<string>('');
return (
<View style={styles.root}>
<View
style={[
styles.inputWrap,
{ borderColor: !value
? '#888'
: value?.length === 11
? '#00CD00'
: '#ff3050' }
]}>
<TextInput
ref={ref}
style={styles.input}
value={value}
keyboardType='number-pad'
onChangeText={value => {
LayoutAnimation.spring();
setValue(value);
}}
maxLength={11}
/>
{!!value &&
<TouchableOpacity
style={styles.deleteButton}
onPress={() => {
LayoutAnimation.spring();
setValue('');
}}
>
<Image style={styles.deleteImg} source={icon_delete} />
</TouchableOpacity>
}
</View>
<View style={styles.tipsLayout}>
{!value ?
<>
<Image style={styles.tipImg} source={icon_question} />
<Text style={styles.tipsTxt}>请输入您的手机号</Text>
</> : value.length === 11 ?
<>
<Image style={styles.tipImgRight} source={icon_right} />
<Text style={styles.tipsTxtRight}>输入正确,可进行提交</Text>
</> :
<>
<Image style={styles.tipImgError} source={icon_error} />
<Text style={styles.tipsTxtError}>格式错误,请输入正确手机号</Text>
</>}
</View>
</View>
);
});
const styles = StyleSheet.create({
root: {
width: '100%',
flexDirection: 'column',
},
input: {
width: '100%',
height: 56,
backgroundColor: 'transparent',
paddingHorizontal: 16,
fontSize: 22,
color: '#333',
},
inputWrap: {
width: '100%',
borderWidth: 2,
borderRadius: 12,
flexDirection: 'row',
alignItems: 'center',
},
tipsLayout: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 6,
paddingHorizontal: 6,
},
tipImg: {
width: 22,
height: 22,
resizeMode: 'contain',
tintColor: '#888',
},
tipsTxt: {
fontSize: 15,
color: '#666',
marginLeft: 6,
fontWeight: 'bold',
},
tipImgRight: {
width: 18,
height: 18,
resizeMode: 'contain',
tintColor: '#00CD00',
},
tipsTxtRight: {
fontSize: 15,
color: '#00CD00',
marginLeft: 6,
fontWeight: 'bold',
},
tipImgError: {
width: 18,
height: 18,
resizeMode: 'contain',
tintColor: '#ff3050',
},
tipsTxtError: {
fontSize: 15,
color: '#ff3050',
marginLeft: 6,
fontWeight: 'bold',
},
deleteButton: {
position: 'absolute',
right: 16,
},
deleteImg: {
width: 24,
height: 24,
resizeMode: 'contain',
borderRadius: 12,
},
});
RefDemo.tsx
import React, { useRef } from 'react';
import {
StyleSheet,
View,
Button,
TextInput
} from 'react-native';
import CustomInput from './CustomInput';
export default () => {
const inputRef = useRef<TextInput>(null);
return (
<View style={styles.root}>
<Button title='聚焦' onPress={() => {
inputRef.current?.focus();
}} />
<Button title='失焦' onPress={() => {
inputRef.current?.blur();
}} />
<CustomInput ref={inputRef}/>
</View>
);
}
const styles = StyleSheet.create({
root: {
width: '100%',
height: '100%',
backgroundColor: 'white',
paddingHorizontal: 20,
paddingTop: 64,
},
});
函数式组件对外暴露实例(通常是api)
CustomInput2.tsx
import React, { useState, useRef, forwardRef, useImperativeHandle } from 'react';
import {
StyleSheet,
View,
Image,
Text,
TextInput,
LayoutAnimation,
TouchableOpacity,
} from 'react-native';
import icon_error from '../assets/images/icon_error.png';
import icon_right from '../assets/images/icon_right.png';
import icon_question from '../assets/images/icon_question.webp';
import icon_delete from '../assets/images/icon_delete.png';
export interface CustomInputRef2 {
customFocus: () => void,
customBlur: () => void,
}
export default forwardRef((props, ref) => {
const inputRef = useRef<TextInput>(null);
const [value, setValue] = useState<string>('');
const customFocus = () => {
console.log('customFocus...')
inputRef.current?.focus();
}
const customBlur = () => {
console.log('customBlur...')
inputRef.current?.blur();
}
useImperativeHandle(ref, () => {
return {
customFocus,
customBlur
};
})
return (
<View style={styles.root}>
<View
style={[
styles.inputWrap,
{ borderColor: !value
? '#888'
: value?.length === 11
? '#00CD00'
: '#ff3050' }
]}>
<TextInput
ref={inputRef}
style={styles.input}
value={value}
keyboardType='number-pad'
onChangeText={value => {
LayoutAnimation.spring();
setValue(value);
}}
maxLength={11}
/>
{!!value &&
<TouchableOpacity
style={styles.deleteButton}
onPress={() => {
LayoutAnimation.spring();
setValue('');
}}
>
<Image style={styles.deleteImg} source={icon_delete} />
</TouchableOpacity>
}
</View>
<View style={styles.tipsLayout}>
{!value ?
<>
<Image style={styles.tipImg} source={icon_question} />
<Text style={styles.tipsTxt}>请输入您的手机号</Text>
</> : value.length === 11 ?
<>
<Image style={styles.tipImgRight} source={icon_right} />
<Text style={styles.tipsTxtRight}>输入正确,可进行提交</Text>
</> :
<>
<Image style={styles.tipImgError} source={icon_error} />
<Text style={styles.tipsTxtError}>格式错误,请输入正确手机号</Text>
</>}
</View>
</View>
);
});
const styles = StyleSheet.create({
root: {
width: '100%',
flexDirection: 'column',
},
input: {
width: '100%',
height: 56,
backgroundColor: 'transparent',
paddingHorizontal: 16,
fontSize: 22,
color: '#333',
},
inputWrap: {
width: '100%',
borderWidth: 2,
borderRadius: 12,
flexDirection: 'row',
alignItems: 'center',
},
tipsLayout: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 6,
paddingHorizontal: 6,
},
tipImg: {
width: 22,
height: 22,
resizeMode: 'contain',
tintColor: '#888',
},
tipsTxt: {
fontSize: 15,
color: '#666',
marginLeft: 6,
fontWeight: 'bold',
},
tipImgRight: {
width: 18,
height: 18,
resizeMode: 'contain',
tintColor: '#00CD00',
},
tipsTxtRight: {
fontSize: 15,
color: '#00CD00',
marginLeft: 6,
fontWeight: 'bold',
},
tipImgError: {
width: 18,
height: 18,
resizeMode: 'contain',
tintColor: '#ff3050',
},
tipsTxtError: {
fontSize: 15,
color: '#ff3050',
marginLeft: 6,
fontWeight: 'bold',
},
deleteButton: {
position: 'absolute',
right: 16,
},
deleteImg: {
width: 24,
height: 24,
resizeMode: 'contain',
borderRadius: 12,
},
});
RefDemo2.tsx
import React, { useRef } from 'react';
import {
StyleSheet,
View,
Button,
} from 'react-native';
import CustomInput2, {CustomInputRef2} from './CustomInput2';
export default () => {
const inputRef = useRef<CustomInputRef2>(null);
return (
<View style={styles.root}>
<Button title='聚焦' onPress={() => {
inputRef.current?.customFocus();
}} />
<Button title='失焦' onPress={() => {
inputRef.current?.customBlur();
}} />
<CustomInput2 ref={inputRef} />
</View>
);
}
const styles = StyleSheet.create({
root: {
width: '100%',
height: '100%',
backgroundColor: 'white',
paddingHorizontal: 20,
paddingTop: 64,
},
});