I am developing a chat, as can be seen from the image.
When the chat opens, the chat scrolls down to show the latest messages.
What I would like to do, that when user scrolls up and gets to the last message (i.e. the oldest one in the chat), the oldMessage function is called which makes an http call passing the current page to try to retrieve the previous messages to the last displayed at the top.
I don't know if I've made myself clear.
Can you give me a hand?
Link: codesandbox
import React, { useState, useRef, useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import {
Card,
Typography,
Icon,
useTheme,
TextField,
IconButton,
Avatar,
Paper
} from "#material-ui/core";
import Moment from "react-moment";
import clsx from "clsx";
import moment from "moment/moment";
const message = [
{
id: 1,
createdAt: "",
message: "Hi, James!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 2,
createdAt: "",
message: "Hi, Vesper!",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 3,
createdAt: "",
message: "Quickly come to the meeting room 1B, we have a big server issue",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 4,
createdAt: "",
message: "I’m having breakfast right now, can’t you wait for 10 minutes?",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 5,
createdAt: "",
message: "I’m having breakfast right now, can’t you wait for 10 minutes?",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 6,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 7,
createdAt: "",
message:
"It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 8,
createdAt: "",
message: "You are the worst!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 9,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 10,
createdAt: "",
message: "You are the worst!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 11,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 12,
createdAt: "",
message:
"It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
}
];
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1)
}
},
messageRow: {
position: "relative",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
justifyContent: "flex-end",
padding: "0 16px 4px 16px",
flex: "0 0 auto",
"&.contact": {
"& $bubble": {
backgroundColor: theme.palette.background.paper,
color: theme.palette.getContrastText(theme.palette.background.paper),
borderTopLeftRadius: 5,
borderBottomLeftRadius: 5,
borderTopRightRadius: 20,
borderBottomRightRadius: 20,
marginLeft: 28,
"& $time": {
marginLeft: 12
}
},
"&.first-of-group": {
"& $bubble": {
borderTopLeftRadius: 20
}
},
"&.last-of-group": {
"& $bubble": {
borderBottomLeftRadius: 20
}
}
},
"&.me": {
paddingLeft: 40,
"& $avatar": {
order: 2,
margin: "0 0 0 16px"
},
"& $bubble": {
marginLeft: "auto",
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
borderTopLeftRadius: 20,
borderBottomLeftRadius: 20,
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
"& $time": {
justifyContent: "flex-end",
right: 0,
marginRight: 12
}
},
"&.first-of-group": {
"& $bubble": {
borderTopRightRadius: 20
}
},
"&.last-of-group": {
"& $bubble": {
borderBottomRightRadius: 20
}
}
},
"&.contact + .me, &.me + .contact": {
paddingTop: 20,
marginTop: 20
},
"&.first-of-group": {
"& $bubble": {
borderTopLeftRadius: 20,
paddingTop: 13
}
},
"&.last-of-group": {
"& $bubble": {
borderBottomLeftRadius: 20,
paddingBottom: 13,
"& $time": {
display: "flex"
}
}
}
},
avatar: {
position: "absolute",
left: 0,
margin: 0
},
bubble: {
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 12,
maxWidth: "100%",
boxShadow: theme.shadows[1]
},
message: {
whiteSpace: "pre-wrap",
lineHeight: 1.2
},
time: {
position: "absolute",
display: "none",
width: "100%",
fontSize: 11,
marginTop: 8,
top: "100%",
left: 0,
whiteSpace: "nowrap"
},
bottom: {
// background: theme.palette.background.default,
// borderTop: '1px solid rgba(0, 0, 0, 0.13)'
},
inputWrapper: {
borderRadius: 24
}
}));
export default function App() {
const classes = useStyles();
const [state, setState] = useState({
userMyInfo: {
id: 1,
name: "James",
surname: "Bond"
},
chat: message,
msgState: "",
pag: 0
});
const { userMyInfo, chat, msgState } = state;
const sendMessage = () => {};
const oldMessage = () => {
//http request
fetch("")
.then((response) => response.json())
.then((message) => {
setState(...(prev) => ({ ...prev, chat: [...message, ...prev.chat] }));
})
.catch((error) => {
console.error(error);
});
};
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
};
useEffect(scrollToBottom, []);
const shouldShowContactAvatar = (item, i) => {
return (
(chat[i + 1] && chat[i].senderId._id !== chat[i + 1].senderId._id) ||
!chat[i + 1]
);
};
const isFirstMessageOfGroup = (item, i) => {
return (
i === 0 || (chat[i - 1] && chat[i - 1].senderId._id !== item.senderId._id)
);
};
const isLastMessageOfGroup = (item, i) => {
return (
i === chat.length - 1 ||
(chat[i + 1] && chat[i + 1].senderId._id !== item.senderId._id)
);
};
return (
<Paper
elevation={3}
className={clsx(classes.root, "flex flex-col relative pb-64")}
>
<Card elevation={1} className="flex flex-col h-512 rounded-8">
<div
className="flex flex-shrink-0 items-center justify-between px-24 h-64"
style={{
background: "#607d8b"
//color: theme.palette.getContrastText('#607d8b')
}}
>
<Typography className="text-center text-16 font-400">Chat</Typography>
</div>
<div style={{ flex: 1, overflowY: "auto" }}>
{state.chat.length === 0 ? (
<div style={{ textAlign: "center" }}>
Al momento non ci sono messaggi
</div>
) : (
state.chat.map((item, key) => (
<div
key={key}
className={clsx(
classes.messageRow,
{ me: item.senderId._id === userMyInfo.id },
{ contact: item.senderId._id !== userMyInfo.id },
{ "first-of-group": isFirstMessageOfGroup(item, key) },
{ "last-of-group": isLastMessageOfGroup(item, key) }
)}
>
{item.senderId._id !== userMyInfo.id &&
shouldShowContactAvatar(item, key) && (
<Avatar className={classes.avatar}>
{item.senderId.name[0]} {item.senderId.surname[0]}
</Avatar>
)}
<div className={classes.bubble}>
<div className={classes.message}>{item.message}</div>
<Typography className={classes.time} color="textSecondary">
{moment(item.time).format("MMMM Do YYYY, h:mm:ss a")}
</Typography>
</div>
</div>
))
)}
<div ref={messagesEndRef} />
</div>
<div style={{ padding: 5, display: "flex", flexDirection: "row" }}>
<TextField
required
id="outlined-required"
label="Message"
//inputRef={textInput}
placeholder="Message"
//onChange={handleChange}
variant="outlined"
fullWidth
/>
<IconButton onClick={() => sendMessage()} disabled={msgState === ""}>
<Icon>send</Icon>
</IconButton>
</div>
</Card>
</Paper>
);
}
You need to add an event handler for scrolling and check you are at the top of the container
const handleScroll = e => {
let element = e.target;
if (element.scrollTop===0) {
//fetch messages
}
}
<div style={{ flex: 1, overflowY: "auto"}} onScroll={ handleScroll}>
Two things you can do is
Monitor for scroll event on an element using onScroll from react
<ScrollableComponent onScroll={this.handleScroll} />
use the windows scroll event handler and detect when user is at top of page
useEffect(() => {
window.addEventListener('scroll', this.handleScroll);
return () => window.removeEventListener('scroll', this.handleScroll);
}
const handleScroll = (event) => {
// code here
}
Related
React component in my app which is just like TextInput which takes input from users and gives auto-complete suggestions like if I try to write node then it will give suggestions dropdown with node.js. This react component is working fine on android but on the ios simulator, it's not working. I am not able to open the keyboard.
On the android simulator, everything is working fine.
<SearchableDropdown
onTextChange={async (text) => {
console.log("text is", text);
await this.loadSearchData(text);
if (text) {
this.setState({
search: true,
});
} else {
this.setState({
search: false,
});
}
}}
check={(text) => {
if (text == true && !this.state.search) {
this.setState({
search: true,
});
}
}}
//On text change listner on the searchable input
onItemSelect={(item) => {
console.log("selected", item.name);
let skills = this.state.skills;
if (!skills.find((skill) => skill == item.name)) {
skills.push(item.name);
}
console.log("Selected skills are", skills);
this.setState({
skills: skills,
});
}}
//onItemSelect called after the selection from the dropdown
containerStyle={{ padding: 0 }}
//suggestion container style
textInputStyle={{
//inserted text style
width: wp("82%"),
padding: 8,
// marginTop: hp('1.67%'),
borderColor: this.state.search ? "#fff" : "#fff",
backgroundColor: this.state.search
? "#fff"
: "#fff",
borderRadius: 6,
}}
itemStyle={{
//single dropdown item style
padding: 10,
//marginTop: 2,
backgroundColor: this.state.search
? "#fff"
: "#FAF9F8",
// borderColor: '#bbb',
// borderWidth: 1,
width: "100%",
}}
itemTextStyle={{
//single dropdown item's text style
color: "#222",
}}
itemsContainerStyle={{
//items container style you can pass maxHeight
//to restrict the items dropdown hieght
opacity: 1,
zIndex: 999,
position: "absolute",
width: wp("91.7%"),
marginTop: hp("7%"),
//height: 100,
// maxHeight: '60%',
borderRadius: 6,
borderWidth: 1,
borderColor: "#BFBEBE",
backgroundColor: "white",
}}
listProps={{
nestedScrollEnabled: true,
}}
items={this.state.skillData}
//mapping of item array
//default selected item index
placeholder={"e.g., Node.js, Python, React..."}
placeholderTextColor={"#BFBEBE"}
//place holder for the search input
resetValue={false}
//reset textInput Value with true and false state
underlineColorAndroid="transparent"
//To remove the underline from the android input
/>
Below is my parent component:
import React from "react";
import {
FlatList,
Image,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { search, trend } from "../../assets/index";
const defaultItemValue = {
name: "",
id: 0,
};
export default class SearchableDropDown extends React.PureComponent {
constructor(props) {
super(props);
this.renderTextInput = this.renderTextInput.bind(this);
this.renderFlatList = this.renderFlatList.bind(this);
this.searchedItems = this.searchedItems.bind(this);
this.renderItems = this.renderItems.bind(this);
this.state = {
item: {},
listItems: [],
focus: false,
};
}
UNSAFE_componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener(
"keyboardDidShow",
this._keyboardDidShow
);
this.keyboardDidHideListener = Keyboard.addListener(
"keyboardDidHide",
this._keyboardDidHide
);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow = () => {
//alert('Keyboard Shown');
// console.log(this.state.focus, 'dd');
this.setState({
focus: true,
});
};
_keyboardDidHide = () => {
//alert('Keyboard Hidden');
this.setState({
focus: false,
item: defaultItemValue,
});
//console.log(this.state);
};
renderFlatList = () => {
if (this.state.focus) {
const flatListPorps = { ...this.props.listProps };
const oldSupport = [
{
key: "check",
val: (text) => {
this.state.focus;
},
},
{ key: "keyboardShouldPersistTaps", val: "always" },
{ key: "nestedScrollEnabled", val: false },
{ key: "style", val: { ...this.props.itemsContainerStyle } },
{ key: "data", val: this.props.items },
{ key: "keyExtractor", val: (item, index) => String(item.id) },
{
key: "renderItem",
val: ({ item, index }) => this.renderItems(item, index),
},
];
oldSupport.forEach((kv) => {
if (!Object.keys(flatListPorps).includes(kv.key)) {
flatListPorps[kv.key] = kv.val;
} else {
if (kv.key === "style") {
flatListPorps["style"] = kv.val;
}
}
});
const check = this.props.check;
setTimeout(() => {
if (typeof check === "function") check(this.state.focus);
});
return <FlatList {...flatListPorps} keyboardShouldPersistTaps="always" />;
}
};
componentDidMount = () => {
const listItems = this.props.items;
const defaultIndex = this.props.defaultIndex;
// if (defaultIndex && listItems.length > defaultIndex) {
// this.setState({
// listItems,
// item: listItems[defaultIndex]
// });
// } else {
this.setState({ listItems });
// }
};
searchedItems = (searchedText) => {
let setSort = this.props.setSort;
if (!setSort && typeof setSort !== "function") {
setSort = (item, searchedText) => {
return item.name.toLowerCase().indexOf(searchedText.toLowerCase()) > -1;
};
}
var ac = this.props.items.filter((item) => {
return setSort(item, searchedText);
});
let item = {
id: -1,
name: searchedText,
};
this.setState({ listItems: ac, item: item });
const onTextChange =
this.props.onTextChange ||
this.props.textInputProps.onTextChange ||
this.props.onChangeText ||
this.props.textInputProps.onChangeText;
if (onTextChange && typeof onTextChange === "function") {
setTimeout(() => {
onTextChange(searchedText);
}, 0);
}
// const check = this.props.check;
// setTimeout(() => {
// if (typeof check === 'function')
// check(this.state.focus);
// })
};
renderItems = (item, index) => {
if (
this.props.multi &&
this.props.selectedItems &&
this.props.selectedItems.length > 3
) {
return this.props.selectedItems.find((sitem) => sitem.id === item.id) ? (
<TouchableOpacity
style={{ ...this.props.itemStyle, flex: 1, flexDirection: "row" }}
>
<View
style={{
flex: 0.9,
flexDirection: "row",
alignItems: "flex-start",
}}
>
<Image style={styles.logo} source={trend} />
{/* */}
<Text>{item.name}</Text>
</View>
<View
style={{ flex: 0.1, flexDirection: "row", alignItems: "flex-end" }}
>
<TouchableOpacity
onPress={() =>
setTimeout(() => {
this.props.onRemoveItem(item, index);
}, 0)
}
style={{
backgroundColor: "#f16d6b",
alignItems: "center",
justifyContent: "center",
width: 25,
height: 25,
borderRadius: 100,
marginLeft: 10,
}}
>
<Text>X</Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={() => {
this.setState({ item: item });
setTimeout(() => {
this.props.onItemSelect(item);
}, 0);
}}
style={{ ...this.props.itemStyle, flex: 1, flexDirection: "row" }}
>
<View
style={{ flex: 1, flexDirection: "row", alignItems: "flex-start" }}
>
<Image style={{ margin: 2 }} source={trend} />
<Text>{item.name}</Text>
</View>
</TouchableOpacity>
);
} else {
return (
<TouchableOpacity
activeOpacity={0.7}
onLongPress={() => console.log("Long pressed")}
style={{ ...this.props.itemStyle }}
onPress={() => {
console.log("Item is", item);
this.setState({ item: item, focus: false });
// this.props.onItemSelect(item);
Keyboard.dismiss();
setTimeout(() => {
this.props.onItemSelect(item);
if (this.props.resetValue) {
this.setState({ focus: true, item: defaultItemValue });
this.input.focus();
}
}, 0);
}}
>
{this.props.selectedItems &&
this.props.selectedItems.length > 3 &&
this.props.selectedItems.find((x) => x.id === item.id) ? null : (
<Image style={{ margin: 2 }} source={trend} />
)}
<Text style={{ ...this.props.itemTextStyle }}>{item.name}</Text>
</TouchableOpacity>
);
}
};
renderListType = () => {
return this.renderFlatList();
};
renderTextInput = () => {
const textInputProps = { ...this.props.textInputProps };
const oldSupport = [
{
key: "check",
val: (text) => {
this.state.focus;
},
},
{ key: "ref", val: (e) => (this.input = e) },
{
key: "onTextChange",
val: (text) => {
this.searchedItems(text);
},
},
{ key: "underlineColorAndroid", val: this.props.underlineColorAndroid },
{
key: "onFocus",
val: () => {
this.props.onFocus && this.props.onFocus();
this.setState({
focus: true,
item: defaultItemValue,
listItems: this.props.items,
});
},
},
{
key: "onBlur",
val: () => {
this.props.onBlur && this.props.onBlur();
this.setState({ focus: false });
},
},
{
key: "value",
val: this.state.item.name,
},
{
key: "style",
val: { ...this.props.textInputStyle },
},
{
key: "placeholderTextColor",
val: this.props.placeholderTextColor,
},
{
key: "placeholder",
val: this.props.placeholder,
},
];
oldSupport.forEach((kv) => {
if (!Object.keys(textInputProps).includes(kv.key)) {
if (kv.key === "onTextChange" || kv.key === "onChangeText") {
textInputProps["onChangeText"] = kv.val;
} else {
textInputProps[kv.key] = kv.val;
}
} else {
if (kv.key === "onTextChange" || kv.key === "onChangeText") {
textInputProps["onChangeText"] = kv.val;
}
}
});
return (
<View
style={{
padding: 6,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: this.state.focus ? "#fff" : "#fff",
borderRadius: 6,
borderWidth: 1,
borderColor: "#BFBEBE",
}}
>
<TextInput pointerEvents="none" onKeyPress={null} {...textInputProps} />
<Image
style={{
marginRight: 10,
}}
source={search}
/>
</View>
);
};
render = () => {
return (
<View
// keyboardShouldPersist="always"
style={{ ...this.props.containerStyle }}
>
{this.renderSelectedItems()}
{this.renderTextInput()}
{this.renderListType()}
</View>
);
};
renderSelectedItems() {
let items = this.props.selectedItems;
if (
items !== undefined &&
items.length > 3 &&
this.props.chip &&
this.props.multi
) {
return (
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
paddingBottom: 10,
marginTop: 5,
}}
>
{items.map((item, index) => {
return (
<View
key={index}
style={{
width: item.name.length * 8 + 60,
justifyContent: "center",
flex: 0,
backgroundColor: "#eee",
flexDirection: "row",
alignItems: "center",
margin: 5,
padding: 8,
borderRadius: 15,
}}
>
<Text style={{ color: "#555" }}>{item.name}</Text>
<TouchableOpacity
onPress={() =>
setTimeout(() => {
this.props.onRemoveItem(item, index);
}, 0)
}
style={{
backgroundColor: "#f16d6b",
alignItems: "center",
justifyContent: "center",
width: 25,
height: 25,
borderRadius: 100,
marginLeft: 10,
}}
>
<Text>X</Text>
</TouchableOpacity>
</View>
);
})}
</View>
);
}
}
}
I don't know why it is not working on my ios simulator. How can I solve this issue?
I have a search bar in my React Native app, without context implementation the list displayed properly. But when I implemented Context, the list was not displaying again, only the search bar was displaying, and when I type on the search bar I get this error message; ERROR TypeError: item.indexOf is not a function. (In 'item.indexOf(str)', 'item.indexOf' is undefined)
I know am missing something, but I don't know where am missing it. Please I need help to solve this issue. Below is my code:
//....
//....
import songs from '../../SongData';
import styles from './styles';
const data = [songs];
const ListContext = React.createContext({
list: [data],
setList: () => {}
});
export const ListProvider = ({ children }) => {
const [list, setList] = useState(data);
return (
<ListContext.Provider value={{list, setList}}>
{children}
</ListContext.Provider>
);
};
const Item = ({ item }) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
};
const Playerlist = () => {
const [isDisabled, setisDisabled] = useState(false);
const [loading, setLoading] = useState(false);
const [isPlayed, setIsPlayed] = useState(false);
const [data, setData] = useState(songs);
//....
//.....
const renderHeader = () => {
const [text, setText] = useState("");
const listContext = useContext(ListContext);
const updateQuery = (str) => {
listContext.setList(data.filter((item) => item.indexOf(str) > -1));
setText(str);
};
return (
<View
style={{
backgroundColor: '#fff',
padding: 10,
marginVertical: 10,
borderRadius: 20
}}
>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
value={text}
onChangeText={updateQuery}
placeholder="Search songs"
style={{ backgroundColor: '#fff', paddingHorizontal: 20 }}
/>
</View>
)
};
const renderItem = ({ item, index }) => (
<ScrollView style={{flex: 1}}>
<View style={styles.item}>
<Avatar
source={{ uri: item.picture }}
style={{ marginRight: 16 }}
size='giant'
/>
<Text style={styles.title} category='s1'> {item.title} </Text>
</View>
<Text
style={{
color: '#444',
fontSize: 11,
marginLeft: 112,
marginVertical: -20,
bottom: 27
}}
category='s1'
>{item.ArtistName}
</Text>
<Text
style={{
color: '#999',
marginLeft: 110,
marginVertical: -20,
top: 10
}}
category='s1'
>Genre: {item.genre}
</Text>
<View style={{flexDirection: 'row', left: '230%', bottom: '7%'}}>
<TouchableOpacity
onPress={()=>playSound(item, index)}
style={{padding: 10, top: 30, left: 30}}
>
<Ionicons name="play" color={isPlayed ? 'red' : '#555555'} size={22} />
</TouchableOpacity>
<TouchableOpacity
onPress={()=>stopSound(index)}
style={{padding: 10, top: 30, left: 20}}
>
<Ionicons name="stop" color="#555555" size={22} />
</TouchableOpacity>
</View>
</ScrollView>
);
const renderFooter = () => {
if (!loading) return null
return (
<View
style={{
paddingVertical: 15,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}
>
<ActivityIndicator animating size='large' />
</View>
);
}
const renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '5%',
}}
/>
)
}
return (
<ListContext.Consumer>
{(context) => (
<SafeAreaView style={styles.container}>
<FlatList
data={context.list}
renderItem={renderItem}
keyExtractor={(i, key) => 'item-' + key}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
/>
</SafeAreaView>
)}
</ListContext.Consumer>
);
};
Here's SongData.js code:
const songs = [
{
id: 1,
title: 'Hero',
ArtistName: 'Bethany Dilon',
genre: 'pop',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.18169-9/16195530_10211997136709517_8578854309931959016_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=09cbfe&_nc_eui2=AeEvt5zlNj1bM87SMIgRXz8VjFbfh8f8mfyMVt-Hx_yZ_ISR6pzt6j1tOqssNCwDfnM&_nc_ohc=oQeQeYLPRz8AX_n81Yh&_nc_ht=scontent-los2-1.xx&oh=d87b3097c543a39067095bacfbeb004d&oe=609BF1DC',
url: require('../../assets/songs/Hero.mp3'),
},
{
id: 2,
title: 'Advertising URL',
ArtistName: 'Bethany Dilon',
genre: 'Soft Rock',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
},
{
id: 3,
title: 'Stronger',
ArtistName: 'Bethany Dilon',
genre: 'Country',
isRequire: true,
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: require('../../assets/songs/Stronger.mp3'),
},
{
id: 4,
title: 'Faded',
ArtistName: 'Luchee',
genre: 'Techno',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3',
},
{
id: 5,
title: 'Solo',
ArtistName: 'Solo Cosmos',
genre: 'Afrobeat',
isRequire: true,
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Solo.mp3',
},
{
id: 6,
title: 'Death Bed',
ArtistName: 'Omowunmi feat Wizkid',
genre: 'Afrocentric',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3',
},
{
id: 7,
title: 'Hero',
ArtistName: 'Bethany Dilon',
genre: 'pop',
isRequire: true,
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.18169-9/16195530_10211997136709517_8578854309931959016_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=09cbfe&_nc_eui2=AeEvt5zlNj1bM87SMIgRXz8VjFbfh8f8mfyMVt-Hx_yZ_ISR6pzt6j1tOqssNCwDfnM&_nc_ohc=oQeQeYLPRz8AX_n81Yh&_nc_ht=scontent-los2-1.xx&oh=d87b3097c543a39067095bacfbeb004d&oe=609BF1DC',
url: require('../../assets/songs/Hero.mp3'),
},
{
id: 8,
title: 'Advertising URL',
ArtistName: 'Bethany Dilon',
genre: 'Soft Rock',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
},
{
id: 9,
title: 'Stronger',
ArtistName: 'Bethany Dilon',
genre: 'Country',
isRequire: true,
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: require('../../assets/songs/Stronger.mp3'),
},
{
id: 10,
title: 'Faded',
ArtistName: 'Luchee',
genre: 'Techno',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3',
},
{
id: 11,
title: 'Solo',
ArtistName: 'Solo Cosmos',
genre: 'Afrobeat',
isRequire: true,
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Solo.mp3',
},
{
id: 12,
title: 'Death Bed',
ArtistName: 'Omowunmi feat Wizkid',
genre: 'Afrocentric',
picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
url: 'https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3',
},
];
export default songs;
I am using react-native-tab-view, and trying to change the indicator width. I would like indicator width to be the same with the tab text. But What I did is just the default. I have tried in many ways, but always it gave me the wrong result. The tab bar should be scrollable horizontally as well. Could you check which part I should change?
This is the expected result :
ShowAllIndex Code :
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.dark,
},
});
const ShowAllIndex = () => {
const { seller } = useSelector((s) => s.auth, shallowEqual);
const [routes] = useState([
{ key: 'best', title: 'BEST' },
{ key: 'jacket', title: '아우터' },
{ key: 'pants', title: '바지' },
{ key: 'skirts', title: '스커트' },
{ key: 'topClothe', title: '원피스' },
{ key: 'one', title: '바지' },
{ key: 'two', title: '스커트' },
{ key: 'three', title: '상의' },
]);
const renderScene = SceneMap({
best: ShowAllMainRoutes,
jacket: JacketRoutes,
pants: PantsRoutes,
skirts: SkirtsRoutes,
topClothe: TopClotheRoutes,
one: ShowAllMainRoutes,
two: JacketRoutes,
three: PantsRoutes,
});
return (
<ScrollView style={[styles.container, { marginTop: Global() }]}>
<CustomTabView
routes={routes}
renderScene={renderScene}
scrollEnabled={true}
tabStyle={{ width: 'auto' }}
showAll={true}
/>
</ScrollView>
);
};
export default ShowAllIndex;
CustomTabView code :
const initialLayout = { width: Dimensions.get('window').width };
const CustomTabView = ({
routes,
renderScene,
numberOfTabs,
indicatorWidth,
scrollEnabled = false,
tabStyle,
showAll,
indicatorStyle,
}) => {
const [index, setIndex] = useState(0);
const renderTabBar = (props) => (
<TabBar
{...props}
scrollEnabled={scrollEnabled}
indicatorStyle={[
indicatorStyle,
{
backgroundColor: colors.barbie_pink,
height: 2.5,
bottom: -1,
},
]}
style={[styles.tabBar]}
renderLabel={({ route, focused }) => {
return (
<Text
style={[styles.label, focused ? styles.activeLabel : styles.label]}
>
{route.title}
</Text>
);
}}
tabStyle={tabStyle}
/>
);
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
renderTabBar={renderTabBar}
onIndexChange={setIndex}
initialLayout={initialLayout}
style={[styles.container]}
/>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.dark,
},
scene: {
flex: 1,
marginTop: 5,
},
tabBar: {
backgroundColor: 'transparent',
shadowOpacity: 0,
elevation: 0,
borderBottomWidth: 0.5,
borderColor: colors.very_light_pink_two,
marginBottom: 5,
},
label: {
color: colors.very_light_pink_four,
fontSize: 14,
lineHeight: 20.8,
fontFamily: 'NotoSansCJKkr-Regular',
letterSpacing: -0.35,
},
activeLabel: {
color: colors.barbie_pink,
},
});
Thank you for answers!
Check this solution:
const TAB_MARGIN = 24;
<TabBar
...
scrollEnabled
renderIndicator={indicatorProps => {
const width = indicatorProps.getTabWidth(this.state.index) - TAB_MARGIN
return <TabBarIndicator {...indicatorProps} width={width} />
}}
indicatorStyle={{
backgroundColor: '#333',
height: 4,
left: TAB_MARGIN / 2,
}}
...
/>
I think the prop indicatorStyle in TabBar that can resolves you problem.
You can do the following:
<TabBar
scrollEnabled //add this line to make it scrollable
tabStyle={{width: 100}} //and this one to change the tab width
/>
I have an array,
const arr = [
{ age: 1, name: 'Raphael' },
{ age: 3, name: 'Inyang' },
{ age: 6, name: 'Ifiok' },
{ age: 8, name: 'Ekpedeme' }
];
I need ages above 5 to have an opacity of 0.5, while the rest will have an opacity of 1
function changeOpacityOfArray(letter) {
if (arr.age >= 5 ) {
letter.style.opacity= '0.5';
}
}
changeOpacityOfArray(arr);
The above doesn't work in JSX,
it says cannot style an undefined element
Then in the HTML body, note the opacity in the styling
<ul style={{listStyleType: 'none'}}>
{arr.map(function(item){
return (<li><div style={{display: 'flex', justifyContent: 'flex-start', width: 'auto', fontSize: 'calc(4px + 2vmin)', opacity: 1, justifyContent: 'flex-start' }}><p>{item.name}: {item.age}</p></div></li>)
}
)}
</ul>
Why don't you move the age check into the style prop?
{arr.map(function (item) {
return (
<li>
<div
style={{
display: "flex",
justifyContent: "flex-start",
width: "auto",
fontSize: "calc(4px + 2vmin)",
opacity: item.age > 5 ? 0.5 : 1,
justifyContent: "flex-start",
}}
>
<p>
{item.name}: {item.age}
</p>
</div>
</li>
);
})}
Also you have a typo in the opacity prop, you have to write it in lowercase
When styling the div you can check the item's age and set the opacity accordingly.
const arr = [
{ age: 1, name: 'Raphael' },
{ age: 3, name: 'Inyang' },
{ age: 6, name: 'Ifiok' },
{ age: 8, name: 'Ekpedeme' },
];
<ul style={{ listStyleType: 'none' }}>
{arr.map(function (item) {
return (
<li>
<div
style={{
display: 'flex',
justifyContent: 'flex-start',
width: 'auto',
fontSize: 'calc(4px + 2vmin)',
opacity: item.age > 5 ? 0.5 : 1,
justifyContent: 'flex-start',
}}>
<p>
{item.name}: {item.age}
</p>
</div>
</li>
);
})}
</ul>;
Here I am populating the JSON value on Flatlist and its working fine ,but now I have to change the color of each list , like in JSON value there is field "criLevel" when its value is 1 it should be normal , if 3 then yellow and 4 then red . So please help me ,how can I do that using if else inside return not working .Please help
import React, { Component } from 'react';
import { View, Text, TextInput,
FooterTab,Button,TouchableOpacity, ScrollView, StyleSheet,
ActivityIndicator ,Header,FlatList} from 'react-native';
import {Icon} from 'native-base';
import { createStackNavigator } from 'react-navigation';
import { SearchBar } from 'react-native-elements';
export default class Issueviewlist extends Component {
static navigationOptions = {
title: 'Selected Item',
header: null,
};
constructor() {
super();
this.state = {
data: null,
loading: true,
search: '',
};
}
componentDidMount() {
this.createViewGroup();
}
createViewGroup = async () => {
try {
const response = await fetch(
'http:///Dsenze/userapi/issue/viewissue',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"password": 'admin',
"username": 'admin',
"startlimit":"0",
"valuelimit":"10",
}),
}
);
const responseJson = await response.json();
const { issueData } = responseJson;
this.setState({
data: issueData,
loading: false,
});
} catch (e) {
console.error(e);
}
};
updateSearch = search => {
this.setState({ search });
};
keyExtractor = ({ id }) => id.toString();
keyExtractor = ({ desc }) => desc.toString();
renderItem = ({ item }) => (
<TouchableOpacity
style={styles.item}
activeOpacity={0.4}
onPress={() => {
this.clickedItemText(item);
}}>
<Text style={styles.buttonText}>Id {item.id}</Text>
<Text>Hospital Name {item.desc}</Text>
<Text>User {item.initiatedBy}</Text>
<Text>Date {item.dateTime}</Text>
</TouchableOpacity>
);
render() {
const { loading, data } = this.state;
return (
<ScrollView>
<View style={styles.container1}>
{this.state.loading ? (
<ActivityIndicator size="large" />
) :
(
<FlatList
data={data}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
)}
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create(
{
container1:
{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
ListContainer :{
borderColor: '#48BBEC',
backgroundColor: '#000000',
color:'red',
alignSelf: 'stretch' ,
},
container2:
{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 15
},
inputBox:{
width:300,
borderColor: '#48BBEC',
backgroundColor: '#F8F8FF',
borderRadius:25,
paddingHorizontal:16,
fontSize:16,
color:'#000000',
marginVertical:10,
},
button:{
width:300,
backgroundColor:'#4169E1',
borderRadius:25,
marginVertical:10,
paddingVertical:16
},
buttonText:{
fontSize:16,
fontWeight:'500',
color:'#ffffff',
textAlign:'center'
},
item:
{
padding: 15
},
text:
{
fontSize: 18
},
button:{
width:300,
backgroundColor:'#4169E1',
borderRadius:25,
marginVertical:10,
paddingVertical:16
},
buttonText:{
fontSize:16,
fontWeight:'500',
color:'red',
textAlign:'center'
},
separator:
{
height: 2,
backgroundColor: 'rgba(0,0,0,0.5)'
}
});
// Below is JSON Response
{
"issueData": [{
"criLevel": 1,
"dateTime": "2018-12-24Z",
"desc": "111",
"id": 1,
"initiatedBy": "1",
"invId": 1,
"issueTypeId": 4,
"statusId": 1
}, {
"criLevel": 1,
"dateTime": "2018-12-24Z",
"desc": "222",
"id": 2,
"initiatedBy": "1",
"invId": 1,
"issueTypeId": 4,
"statusId": 1
}, {
"criLevel": 3,
"dateTime": "2018-12-24Z",
"desc": "222",
"id": 3,
"initiatedBy": "1",
"invId": 1,
"issueTypeId": 4,
"statusId": 1
}, {
"criLevel": 4,
"dateTime": "2018-12-24Z",
"desc": "222",
"id": 4,
"initiatedBy": "1",
"invId": 1,
"issueTypeId": 4,
"statusId": 1
}],
"success": "true"
}
Thanks
You can change the item style depending on the "criLevel"
renderItem = ({ item }) => (
<TouchableOpacity
style={[styles.item, {color: getColor(item)}}
activeOpacity={0.4}
onPress={() => {
this.clickedItemText(item);
}}>
<Text style={styles.buttonText}>Id {item.id}</Text>
<Text>Hospital Name {item.desc}</Text>
<Text>User {item.initiatedBy}</Text>
<Text>Date {item.dateTime}</Text>
</TouchableOpacity>
);
getColor = (item) => {
switch(item.criLevel) {
case 3:
return "yellow"
....
}
}