How I make this input in react native customable? - react-native

I need a verification input and want to make it like in this picture:
I know how to make it but not customable. Maybe I want to make 4 or 5.
I can make it like this
const input2 = useRef();
const input3 = useRef();
const input4 = useRef();
const input5 = useRef();
<TextInput onChangeText={(e) => (setText(e), input2.focus() ) } />
<TextInput ref={(input) => { input2 = input } } />
But then its hardcoded. How can I make it customable ?

You could use react-native-confirmation-code-field which is highly customizable.
Here is a basic example using 4 inputs instead of 5. In fact, you can use any number of inputs.
const [value, setValue] = useState("")
const ref = useBlurOnFulfill({ value, cellCount: 4 })
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
})
return (
<SafeAreaView style={{ margin: 40, marginTop: 80 }}>
<CodeField
ref={ref}
{...props}
// Use `caretHidden={false}` when users can't paste a text value, because context menu doesn't appear
value={value}
onChangeText={setValue}
cellCount={4}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<Text
style={{
width: 40,
height: 40,
lineHeight: 38,
fontSize: 24,
borderWidth: 2,
borderColor: "#00000030",
textAlign: "center",
}}
key={index}
onLayout={getCellOnLayoutHandler(index)}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
)}
/>
</SafeAreaView>
In general this component is very customizable. You can render your own input components, etc.
The above code yields the following very simple output. Designing the exact same component as given in your picture boils to designing a custom cell inside the render function.

Related

how to get list index using react-native-swiper-flatlist?

the library quotes the getCurrentIndex function, but I can't implement it in my code. Could someone show me an example of use? I couldn't find it anywhere.
You need to pass a ref via the ref prop to the component. This will give you access to the getCurrentIndex function.
Here is a minimal example.
const data = [0, 1, 2, 3, 4]
const App = () => {
const scrollRef = React.useRef(null);
return <View style={styles.container}>
<SwiperFlatList
ref={scrollRef}
data={data}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => console.log(scrollRef.current.getCurrentIndex())} style={{width: 200, height: 200, backgroundColor: "red"}}>
</TouchableOpacity>
)}
/>
</View>
};
The above will print the current index onPress of the rendered item. The key part is scrollRef.current.getCurrentIndex().

How to create a sticky tab selector with nested scrollviews like twitter or instagram profile screens [REACT-NATIVE]

I'm trying to create a ScrollView which contains one sticky selector, that allow the selection between two nested ScollViews. It's like the twitter profile screen, or the instagram screen, where you can switch between my posts and posts where I was tagged.
Now my problem actually is that this two nested ScollViews, let's say "MY POSTS" and "TAGGED" could have different sizes, but the RootScrollView consider only the biggest height of the two scrollviews, so if in the first I've 20 items, and let's say height=1000, in the second if I don't have items, or less items, I'll have an empty space y offset like the first.
I know it's not so clear, but if you open instagram or twitter profile screens you'll immediately get it, the problem of the different heights.
Now as you'll see, what I've tried to do is create a RootScrollView, put inside it two views, the header and the sticky selector, in twitter it's the "Tweet", "Tweets and replies" ... , and the a NestedScrollView which initially has scrollEnabled=false, and then, by scroll the root I'll update it to true and to false the root one. But it seems not to work correctly.
Here's the code:
const HEADER_HEIGHT = height / 3;
const STIKY_SELECTOR_HEIGHT = 100;
const App = () => {
const rootScrollRef = useRef();
const nestedScrollRef = useRef();
const [offset, setOffset] = useState(0);
const [scrollEnabled, setScrollEnabled] = useState(false);
const onRootScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
const direction = y > offset ? "down" : "up";
setOffset(y);
if (y > HEADER_HEIGHT - 10 && direction == "down") {
setScrollEnabled(true);
}
};
const onNestedScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
if (y < 20) setScrollEnabled(false);
};
const renderItem = () => {
return <View style={styles.cell} />;
};
return (
<View style={{ flex: 1 }}>
{/* ROOT SCROLLVIEW */}
<ScrollView
simultaneousHandlers={nestedScrollRef}
scrollEventThrottle={16}
ref={rootScrollRef}
onScroll={onRootScroll}
stickyHeaderIndices={[1]}
scrollEnabled={!scrollEnabled}
style={{ flex: 1, backgroundColor: "gray" }}
>
{/* HEADER */}
<View
style={{ width, height: HEADER_HEIGHT, backgroundColor: "darkblue" }}
></View>
{/* STIKY SELECTOR VIEW */}
<View
style={{ height: STIKY_SELECTOR_HEIGHT, backgroundColor: "red" }}
></View>
{/* NESTED SCROLLVIEW */}
<View style={{ height: height - STIKY_SELECTOR_HEIGHT }}>
<FlatList
data={[1, 2, 3, 4, 5, 6, 7]}
ref={nestedScrollRef}
scrollEventThrottle={16}
onScroll={onNestedScroll}
scrollEnabled={scrollEnabled}
renderItem={renderItem}
numColumns={2}
contentContainerStyle={{
justifyContent: "space-between",
}}
/>
</View>
</ScrollView>
</View>
);
};
If someone is facing the same problem there a component for that react-native-collapsible-tab-view
<Tabs.Container
renderHeader={Header}
headerHeight={HEADER_HEIGHT} // optional>
<Tabs.Tab name="A">
<Tabs.FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={identity}
/>
</Tabs.Tab>
<Tabs.Tab name="B">
<Tabs.ScrollView>
<View style={[styles.box, styles.boxA]} />
<View style={[styles.box, styles.boxB]} />
</Tabs.ScrollView>
</Tabs.Tab>
</Tabs.Container>

Is there a way to get past the error "TypeError: headers.map is not a function"?

TypeError: headers.map is not a function
function TableComponent(headers, values) {
if (!headers || !values) return null;
const optionsPerPage = [2, 3, 4];
const [page, setPage] = useState(0);
const [itemsPerPage, setItemsPerPage] = useState(optionsPerPage[0]);
console.log(headers);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<DataTable>
{headers.map(({ title, numeric }) => <DataTable.Title key={title} numeric={numeric}>{title}</DataTable.Title>)}
{values.map((value, index) => <DataTable.Row key={index}>
{headers.map(({ title }) => <DataTable.Cell key={title}>{value[title]}</DataTable.Cell>)}
</DataTable.Row>)}
<DataTable.Pagination
page={page}
numberOfPages={3}
onPageChange={(page) => setPage(page)}
label="1-2 of 6"
optionsPerPage={optionsPerPage}
itemsPerPage={itemsPerPage}
setItemsPerPage={setItemsPerPage}
showFastPagination
optionsLabel={'Rows per page'}
/>
</DataTable>
</View>
);
}
Maybe I am using the wrong solution for this problem entirely. I am open to alternatives if this simply cannot work.
Per the comments, it looks like headers is an object and not an array, which explains "headers.map is not a function". Instead, use headers.headers.map(...) because headers.headers is an array.

Component only reachable by scrolling regardless of window size

I'm trying to make a layout so that the later parts of the view are only reachable by scrolling.
Currently I'm using Dimensions to generate Views with the correct height. Is there a better way of doing so? My current solution doesn't seem too correct.
export default function MyApp() {
const height = Dimensions.get('window').height;
return (
<View style={styles.container}>
<ScrollView>
<View style={{backgroundColor: 'green', height:height}}/>
<View style={{backgroundColor: 'red', height:40}}/>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container:{
backgroundColor: 'white',
flex: 1
}
});
You can use VirtualizedList component, for example as
<VirtualizedList
data={['body']}
renderItem={({ item }) => (
<View style={styles.screen}>
{/* Put more content for body */}
</View>
)}
keyExtractor={(item, index) => index.toString()}
getItemCount={() => {
return 1;
}}
getItem={(data, index) => {
return data[index];
}}>
</VirtualizedList>
Your solution work, but not good and it have downside, when you change your phone orientation to landscape there will be bug. I dont like using Dimensions in my code unless there is no other way or use Dimensions addEventListener to listen window size and update component whenever window size change. I will suggest you a better way.
First, create a component called LayoutSizeAwareView, after this view rendered, we will catch it size from onLayout props and use them to render it children.
const LayoutSizeAwareView = (props) => {
const [size, setSize] = React.useState({width: 0, height: 0});
return (
<View
...props,
onLayout={(e) => {
setSize({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
})
props.onLayout(e)
}}
>
{props.children(size)}
</View>
)
}
And then, in your case, use it like this
export default function MyApp() {
return (
<LayoutSizeAwareView style={styles.container}>
{({width, height}) => {
return (
<ScrollView>
<View style={{backgroundColor: 'green', height: height}}/>
<View style={{backgroundColor: 'red', height: 40}}/>
</ScrollView>
)
}}
</View>
);
}
const styles = StyleSheet.create({
container:{
backgroundColor: 'white',
flex: 1
}
});
This way your code look even cooler, there will be some typo in my code since I dont have IDE here, but you might get the idea.

React Native TabNavigator change TabStyle to follow according to the text

I am using expo v27.0, react native 0.55 and I as you can see in the picture that the tab have somewhat a fixed width like a default width from the tab navigation, and the text wrap into three lines, I want the text to be in 1 line and nowrap, and i have tried styling (flexWrap:
'nowrap', flex: 1) in TabStyle, LabelStyle in TabBarOptions, but still can't get the tab to have the width according to the text inside the tab.
I populate the text for the tabs dynamically from json using fetch, therefore all tabs will have different width according to the text. How to I make the tab to follow the width of the text ?
All answers are greatly welcomed.
Thank you in advance.
Solved, turns out just need to set the width to auto as follows:
tabBarOptions: {
tabStyle: {
width: 'auto'
}
}
You can use render label in render header and in that you can return your Text component and Text is having numberOfLines props that will be 1 and it will add ... at end of the text after one line.
Check example snippet:
_renderLabel = props => {
let preparedProps = {
style: {
fontFamily: fonts.Regular,
marginVertical: 8
},
fontType: props.focused ? "Medium" : "Light"
};
return (
<Text
{...preparedProps}
numberOfLines={1}
ref={ref => {
ref && this.props.addAppTourTarget(ref, props.route.key);
}}
>
{props.route.type === "free" && this.state.is_premium_member
? this.labels.premium
: props.route.title}
</Text>
);
};
_renderHeader = props => (
<TabBar
{...props}
bounces={true}
style={{
backgroundColor: colors.cardBlue
}}
indicatorStyle={{
backgroundColor: colors.radicalRed,
height: 1,
borderRightWidth: initialLayout.width * 0.1,
borderLeftWidth: initialLayout.width * 0.1,
borderColor: colors.cardBlue
}}
tabStyle={{
padding: 0,
borderTopColor: "transparent",
borderWidth: 0
}}
renderLabel={this._renderLabel}
/>
);
_handleIndexChange = index => this.setState({ index });
_renderScene = ({ route, focused }) => {
switch (route.key) {
case "a":
return <One {...this.props} route={route} focused={focused} />;
case "b":
return (
<Two {...this.props} isSeries={true} focused={focused} />
);
case "c":
return <Three {...this.props} route={route} focused={focused} />;
default:
return null;
}
};