When I close the modal, I need to detect that it has been closed to change the state of the parent page. Not being able to change it when I change any property of the state, the modal.
ExpertFeedback.js
import ModalExpertFeedback from './ModalExpertFeedback';
export default class ExpertFeedback extends Component {
constructor(props) {
super(props);
this.state = {
modalVisible: false,
projects: [{name:'project0', name:'project1'}],
feedback: {title: '', content: '', project_id: ''}
};
}
proveProjectIsntNull() {
if (this.state.feedback.project_id != null){
this.setModalVisible(true);
} else {
this.setModalVisible(false);
Alert.alert('Select a project please');
}
}
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View>
<View>
<TextInput
placeholder="Write title"
onChangeText={(feedback_title) => this.setState( prevState => ({
feedback: {
...prevState.feedback,
title: feedback_title
}}))
}
value={this.state.feedback.title}
/>
<Picker
selectedValue={this.state.feedback.project_id}
onValueChange={(itemValue, itemIndex) => this.setState( prevState => ({
feedback: {
...prevState.feedback,
project_id: itemValue
}}))
}>
<Picker.Item label="Select a project" value={null} />
{typeof this.state.projects === 'object' && this.state.projects.length && (this.state.projects.map((project, index) => {
return (<Picker.Item label={project.name} value={project.id} />)
}))}
</Picker>
</View>
<ModalExpertFeedback visible={this.state.modalVisible} navigation={this.props.navigation} feedback={this.state.feedback} />
<TouchableOpacity
onPress={() => {
this.proveProjectIsntNull();
}}>
<View>
<Text>SEND NOW</Text>
</View>
</TouchableOpacity>
</View>
)
}
}
ModalExpertFeedback.js
export default class ExpertFeedback extends Component {
feedback = {
title: "",
content: "",
project_id: "",
};
state = {
modalVisible: false
};
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
componentWillReceiveProps(props) {
this.setState({ modalVisible: props.visible});
this.setState({ feedback: props.feedback });
}
render() {
return (
<View>
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => { console.log('close') }} >
<View>
<TouchableOpacity
onPress={() => {
this.setModalVisible(false);
}}
>
<View>
<Text>Close</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
</View>
)
}
}
When I change feedback.title with TextInput in ExpertFeedback, the Modal opens
If you want to 'connect' the Parent and the Child, you'll need to pass a handler, essentially a function as a prop from your Parent to your child.
Example below:
ExpertFeedback.js
parentHandler(result){
//do your update here
this.setState({result});
}
<ModalExpertFeedback
visible={this.state.modalVisible}
navigation={this.props.navigation}
feedback={this.state.feedback}
handler={this.parentHandler.bind(this)} />
ModalExpertFeedback.js
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => { this.props.handler(someValue) }} >
Related
How to limit the quantity of View inside of a scrollview.
My component take too much time to render, because the map function renders too many views. I need to show only 10 views, and when scroll up, renders more 10.
I'm using react native, hooks and typescript.
First of all, if you have a large number of list data don't use scrollview. Because initially, it loads all the data to scrollview component & it costs performance as well.
Use flatlist in react-native instead of scrollview & you can limit the number of items to render in the initially using initialNumToRender. When you reach the end of the scroll position you can call onEndReached method to load more data.
A sample will like this
import React, { Component } from "react";
import { View, Text, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
};
handleRefresh = () => {
this.setState(
{
page: 1,
seed: this.state.seed + 1,
refreshing: true
},
() => {
this.makeRemoteRequest();
}
);
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.makeRemoteRequest();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
</List>
);
}
}
export default FlatListDemo;
Check this for more informations.
I changed to Flatlist! But initialNumToRender is not working as expected.
The flatlist is rendering all 150 transactions, not only 15, and i have no idea what to do.
I'm running .map() from another array with all others transactions to create newMonths with only those transactions that i want to use data={newMonths}.
let newMonths = [];
const createArrayMonth = histInfos.map(function (info) {
if (info.created_at.slice(5, 7) === month) {
newMonths = [info].concat(newMonths);
}
});
them, i created my component:
function Item({ value }: { value: any }) {
let createdDay = value.item.created_at.slice(8, 10);
let createdMonth = value.item.created_at.slice(5, 7);
let createdYear = value.item.created_at.slice(2, 4);
function dateSelected() {
if (
month === createdMonth &&
year === createdYear &&
(day === '00' || day == createdDay)
) {
console.log('foi dateSelected');
const [on, setOn] = useState(false);
const Details = (on: any) => {
if (on === true) {
return (
<View style={styles.infos}>
<Text style={styles.TextInfos}>
{' '}
CPF/CNPJ: {value.item.cpf_cnpj}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Criado em: {value.item.created_at}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Endereço da carteira: {value.item.address}{' '}
</Text>
<Text style={styles.TextInfos}> Valor: {value.item.amount}BTC </Text>
</View>
);
} else {
return <View />;
}
};
return (
<View>
<TouchableOpacity
style={styles.card}
onPress={() => setOn(oldState => !oldState)}>
<View style={styles.dateStyleView}>
<Text style={styles.dateStyle}>{createdDay}</Text>
</View>
<View style={styles.left}>
<Text style={styles.title}>Venda rápida</Text>
<Text style={styles.semiTitle}>
{
{
0: 'Pendente',
1: 'Aguardando conclusão',
2: 'Cancelado',
100: 'Completo',
}[value.item.status]
}
</Text>
</View>
<View style={styles.right2}>
<Text style={styles.price}>R$ {value.item.requested_value}</Text>
</View>
</TouchableOpacity>
<View>{Details(on)}</View>
</View>
);
}
}
return dateSelected();}
and i call it here
return (
<ScrollView>
...
<View style={styles.center}>
...
<View style={styles.middle2}>
...
<FlatList
extraData={[refresh, newMonths]}
data={newMonths}
renderItem={(item: any) => <Item value={item} />}
keyExtractor={(item, index) => index.toString()}
initialNumToRender={15}
/>
</View>
</View>
</ScrollView>);}
The scroll bar in the right, start to increase until renders all transactions from the data:
App scroll bar
I want to have a default string for a nested prop within my component, but the following isn't showing the default value:
export default SuccessModal = ({
completed,
setCompleted,
navigation,
isOpen,
setModal
}) => {
const { data, error } = completed && completed
const onPressHandler = () => {
setModal(false)
navigation.navigate('Profile')
}
return (
<View style={styles.container}>
<Modal
animationType="fade"
transparent={false}
visible={true}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={styles.modal}>
<View>
{error ? <Text style={styles.mainText}>Sorry, something went wrong!</Text>
: <Text style={styles.mainText}>Your {data.createPost && data.createPost.title} has been posted!</Text>}
<TouchableHighlight
style={styles.button}
onPress={onPressHandler}>
<Text style={styles.buttonText}>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
)
}
SuccessModal.propTypes = {
completed: PropTypes.shape({
data: PropTypes.shape({
createPost: PropTypes.shape({
title: PropTypes.string.isRequired
})
})
}),
setCompleted: PropTypes.func,
navigation: PropTypes.object,
isOpen: PropTypes.bool,
setModal: PropTypes.func,
}
SuccessModal.defaultProps = {
completed: {
data: {
createPost: {
title: "post",
}
}
}
}
I want the title nested inside the completed object to show "post", but when the component is rendered, the value doesn't show, but instead shows blank
You could always try to make it within the code using javascript evaluation like here:
<Text style={styles.mainText}>Your {(data.createPost && data.createPost.title) || "post"} has been posted!</Text>}
I'm creating a small react native app, when i add some code to update some data from firebase it shows me this error on console:
"FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: undefined"
Code of my action:
const updateChat =(newChat)=>{
return (dispatch)=>{
console.log("trying to update: ", newChat);
console.log("trying to update and getting the id: ", newChat.id);
firestore.firestore().collection("chat").doc(newChat.id)
.update(
{
msg: newChat,
}
)
.then(() =>{
dispatch({
type:'UPDATE_CHAT',
})
})
.catch(function(error) {
console.error("Error updating document: ", error);
})
}}
Code of my component:
class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'Chat Screen',
};
state = {
id: "",
chat_input: "",
updated: false,
}
onNewChat = () => {
this.props.addChat(
this.state.chat_input
)
this.setState({
chat_input: ""
});
Keyboard.dismiss();
}
handleUpdate = (id, chat_input) => {
this.setState(
{
id:id,
chat_input: chat_input,
updated: true,
}
)
}
saveUpdate=()=>{
this.props.updateChat(this.state.chat_input)
this.setState({
chat_input: "",
id: "",
})
}
renderItem = ( {item} ) => {
return (
<View style={styles.row}>
<Text style={styles.message} >{item.msg}</Text>
<TouchableOpacity
style={styles.button}
onPress={ () => {this.props.deleteChat(item.id)} }
>
<Image
source={require('../assets/images/trash2.png')}
fadeDuration={0}
style={{width: 30, height: 30}}
/>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonEdit}
onPress={ () => { this.handleUpdate(item.id, item.msg} }
>
<Image
source={require('../assets/images/edit.png')}
fadeDuration={0}
style={{width: 30, height: 30}}
/>
</TouchableOpacity>
</View>
);
}
render() {
const { thread } = this.props || []
if (!thread) {
return (
<View style={styles.container}>
<Text>Loading...</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={thread}
renderItem={this.renderItem}
inverted
keyExtractor={(item, index) => index.toString()}
/>
<KeyboardAvoidingView behavior="padding">
<View style={styles.footer}>
<TextInput
value={this.state.chat_input}
onChangeText={text => this.setState({ chat_input: text })}
style={styles.input}
underlineColorAndroid="transparent"
placeholder="Type something nice"
/>
<TouchableOpacity onPress={
this.state.updated
? this.saveUpdate()
: this.onNewChat.bind(this)
}
>
<Text style={styles.send}>Send</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
thread: state.firestore.ordered.chat
}
}
export default compose(
connect(mapStateToProps, {addChat, deleteChat, updateChat}),
firestoreConnect([
{ collection: 'chat'},
]))(SettingsScreen);
You're not passing all the information to your updateChat action. Edit like below
saveUpdate=()=>{
this.props.updateChat({
chatInput: this.state.chat_input,
id: this.state.id
})
this.setState({
chat_input: "",
id: "",
})
}
You'll also need to change your update parameters to this:
firestore.firestore().collection("chat").doc(newChat.id)
.update(
{
msg: newChat.chatInput,
}
)
I am studying React Naive by making a ToDo list.
I want to change the ToDo app to a Comment List that has comment one to another.
First attempt worked correctly:
First attempt
But, all users changed after second attempt.
Second attempt
Now, parent passes props to child by using {this.state.pick} and {this.state.key} , but child would change if parent's props changed.
Is there any way to change the parent props without changing child props?
Comment.js:
export default class CommentIndex extends Component<{}> {
constructor(props) {
super(props);
this.state = {
head: [],
list: [],
pick: [],
};
}
_onPress = (text) => {
const list = [].concat(this.state.list);
list.push({
key: Date.now(),
text: text,
done: false,
});
this.setState({
list,
});
}
render() {
const {
head,
list,
pick,
} = this.state;
var data = [["User1", "User2", "User3"],];
return (
<View style={styles.container}>
<View style={styles.dropdownHeader}>
<View style={styles.dropdown}>
<DropdownMenu
style={{flex: 1}}
bgColor={'white'}
tintColor={'#666666'}
activityTintColor={'green'}
handler={(selection, row) => this.setState({head: data[selection][row]})}
data={data}
>
<View style={styles.userHeader}>
{ this.state.head === 'User1' && <User1 /> }
{ this.state.head === 'User2' && <User2 /> }
{ this.state.head === 'User3' && <User3 /> }
</View>
</DropdownMenu>
</View>
</View>
<Text>To Do</Text>
<View style={styles.main}>
<View style={styles.post}>
<View style={styles.dropdown}>
<View style={{height: 0}} />
<DropdownMenu
bgColor={'white'}
tintColor={'#666666'}
activityTintColor={'green'}
handler={(selection,row) => this.setState({pick: data[selection][row]})}
data={data}
>
<View style={styles.user}>
{ this.state.pick === 'User1' && <User1_photo /> }
{ this.state.pick === 'User2' && <User2_photo /> }
{ this.state.pick === 'User3' && <User3_photo /> }
</View>
</DropdownMenu>
</View>
<View style={styles.postinput}>
<CommentInput onPress={this._onPress} />
</View>
</View>
<View style={styles.CommentListContainer}>
<FlatList
style={styles.CommentList}
data={list}
renderItem={({ item }) => <CommentItem {...item} head={this.state.head} pick={this.state.pick}/>}
/>
</View>
</View>
</View>
);
}
}
CommentInput:
export default class CommentInput extends Component {
constructor(props) {
super(props);
this.ref = {};
}
_onPress = () => {
this.props.onPress(this.ref._lastNativeText);
this.ref.setNativeProps({ text: '' });
}
render() {
const {
onPress,
} = this.props;
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
ref={(ref) => { this.ref = ref; }}
/>
<TouchableOpacity
style={styles.button}
onPress={this._onPress}
>
<Text style={styles.buttonText}>追加</Text>
</TouchableOpacity>
</View>
);
}
}
You could implement the shouldComponentUpdate method on the child. This will block a re-render (with the new properties) when you want.
whats is the problem in my code?
i can't return list view ? If there is a better strategy, please advise me to make my code better
class Search extends Component {
static navigationOptions =
{
title: 'search',
headerBackTitle: null,
};
state = {
search : '',
output : []
}
handleSearch = (text) => {
this.setState({search: text})
}
searchMe = (search) => {
this.setState({output: <SearchExtend />})
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.input}
onChangeText={this.handleSearch}
>
</TextInput>
<TouchableOpacity
style={styles.submitButton}
onPress={
()=>this.searchMe(this.state.search)
}
>
<Text>Submit</Text>
</TouchableOpacity>
<View>{this.state.output}</View>
</View>
);
}
}
ActivityIndicator is displayed but the listview does not output
class SearchExtend extends Component{
constructor(props) {
super(props);
this.state = {
isLoading: true
}
}
componentDidMount() {
return fetch('http://example.com/games.php')
.then((response) => response.json())
.then((responseJson) => {
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
isLoading: false,
dataSource: ds.cloneWithRows(responseJson),
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
onPress(item, rowData){
if(item == 0)
{
this.props.navigation.navigate('Third', {rowData},)
}
else {
this.props.navigation.navigate('Second', {rowData},)
}
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) =>
<TouchableOpacity
onPress={()=> this.onPress(rowData.subCategory, rowData)}>
<Text>{rowData.name}{rowData.subCategory != 0 ? '>' : ''}</Text>
</TouchableOpacity>
}
removeClippedSubviews={false}
/>
</View>
);
}
}
Please help me to fix this problem
The rest of the sections and styles were removed because they were not related to the question