I'm successfully generating a FlatList from Realm with no issues. However, when I close my modal screen after adding a new record, the data doesn't refresh automatically (however it does after a "Pull to Refresh"). Can someone help me understand what needs to be called in the "HOW_TO_REFRESH" placeholder?
FlatList Generation:
export default class Accounts extends Component {
static propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({
accountid: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired
})),
onDeleteItem: PropTypes.func,
onRefreshItems: PropTypes.func,
onSelectItem: PropTypes.func,
refreshing: PropTypes.bool
};
static defaultProps = {
onDeleteItem: (item) => {
realm.write(() => {
realm.delete(realm.objectForPrimaryKey('Account', item.accountid));
})
},
onRefreshItems: () => { console.log('Refresh accounts'); },
onSelectItem: (item) => { console.log('onSelectItem ', item); },
refreshing: false
}
constructor(props) {
super(props);
this.state = ({
activeRow: null
});
}
renderItem(info, activeRow) {
return (
<AccountListItem item={info.item} onPress={() => this.props.onSelectItem(info.item)} />
);
}
render() {
const listSettings = {
data: realm.objects('Account'),
extraData: this.state.activeRow,
keyExtractor: (item, index) => item.accountid,
onRefresh: this.props.onRefreshItems,
refreshing: this.props.refreshing,
renderItem: (info) => this.renderItem(info, this.state.activeRow)
};
return (
<Container style={styles.container}>
<Header>
<Left></Left>
<Body>
<Title>Accounts</Title>
</Body>
<Right>
<Button transparent
onPress={() => this.props.navigation.navigate('AddAccount', {name: 'Accounts', onGoBack: () => HOW_TO_REFRESH})} >
<Icon name="ios-add" />
</Button>
</Right>
</Header>
<Container>
<FlatList {...listSettings} />
</Container>
</Container>
);
}
}
Called in my modal to navigate back:
navigation.goBack();
navigation.state.params.onGoBack();
define your own method
goBack= () => {
//do code for refresh flatlist
}
mention it when you are navigating
this.props.navigation.navigate('AddAccount', {name: 'Accounts', onGoBack: () => this.goBack})
Related
I am trying to activate a fab button to have options on a screen, in addition to that my screen has a list of REACT-NATIVE-PAPER cards, but it cannot hide the card behind the shadow of the modal, it is as if it had a elevation or a very high zIndex, I tried to decrease the zIndex but it doesn't work, thanks, this is a image of project https://i.stack.imgur.com/7aNns.jpg
import React, { Component } from 'react';
import { FAB } from 'react-native-paper';
import { View, ScrollView } from 'react-native';
import AppbarNavigation from '../../components/appbar_navegation'
import ProjectCard from '../../components/project_card'
import styles from './style'
class Home extends Component {
state = {
open: false,
};
_onStateChange = ({ open }) => this.setState({ open });
render() {
const { navigation } = this.props;
const { open } = this.state;
return (
<>
<AppbarNavigation
title='Proyectos'
search
searchfunction={() => console.log("funcion de buscar")}
navigation={() => { navigation.toggleDrawer(); }}
/>
<FAB.Group
style={styles.fabgroup}
open={open}
disabled
icon={open ? 'close' : 'plus'}
actions={[
{ icon: 'star', label: 'Star', onPress: () => console.log('Pressed star') },
{ icon: 'email', label: 'Email', onPress: () => console.log('Pressed email') },
{ icon: 'bell', label: 'Remind', onPress: () => console.log('Pressed notifications') },
]}
onStateChange={this._onStateChange}
onPress={() => {
if (open) {
}
}}
/>
<View>
<ScrollView [style={styles.scrollView}][1]>
<View style={styles.menuContainer}>
<ProjectCard
imageProject={{ uri: 'https://picsum.photos/700' }}
nameProject='proyecto'
projectClient='Name User'
projectProperty='NPH'
projectModificate='13 Min'
projectAuthor='Miller Watson'
/>
</View>
</ScrollView>
</View>
</>
);
}
}
export default Home;
The solution is the components orden, so the script is update to this:
export default class Home extends Component {
state = {
open: false,
};
_onStateChange = ({ open }) => this.setState({ open });
render() {
const { navigation } = this.props;
const { open } = this.state;
return (
<>
<AppbarNavigation
title='Proyectos'
search
searchfunction={() => console.log("funcion de buscar")}
navigation={() => { navigation.toggleDrawer(); }}
/>
<View>
<ScrollView [style={styles.scrollView}][1]>
<View style={styles.menuContainer}>
<ProjectCard
imageProject={{ uri: 'https://picsum.photos/700' }}
nameProject='proyecto'
projectClient='Name User'
projectProperty='NPH'
projectModificate='13 Min'
projectAuthor='Miller Watson'
/>
</View>
</ScrollView>
</View>
<FAB.Group
style={styles.fabgroup}
open={open}
disabled
icon={open ? 'close' : 'plus'}
actions={[
{ icon: 'star', label: 'Star', onPress: () => console.log('Pressed star') },
{ icon: 'email', label: 'Email', onPress: () => console.log('Pressed email') },
{ icon: 'bell', label: 'Remind', onPress: () => console.log('Pressed notifications') },
]}
onStateChange={this._onStateChange}
onPress={() => {
if (open) {
}
}}
/>
</>
);
}
}
I am trying to create a searchable flatlist on this new app I was working on over quarantine. I followed an article on the internet on how to create it and my code so far looks like this:
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;
This works and all, but how could I alter the makeRemoteRequest function so that I got data from a local json file instead of a url? For example:
makeRemoteRequest = () => {
const url = `../data/userData.json`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
I tried this with no success as none of the json data rendered and appeared in the flatlist
The Fetch API is a promise-based JavaScript API for making asynchronous HTTP requests in the browser similar to XMLHttpRequest. If you want to load data from a local JSON file.
First import that file
import Data from '../data/userData.json'
Then assign imported value to your state inside your makeRemoteRequest function
makeRemoteRequest = () => {
this.setState({
data: Data,
});
}
Hope this helps you. Feel free for doubts.
I am trying to navigates screens with a stack navigator. The Idea is, my app will navigate from my list of chats, too the chat screen. However when I try to navigate to the next screen, I receive an error saying "undefined is not an object" on this.props.navigation. Here is what my code looks like:
MainTabNavigator (Contains my stack navigator)
const ChatListStack = createStackNavigator({
ChatList:ChatListScreen,
ChatView:ChatScreen,
});
ChatListStack.navigationOptions = {
tabBarLabel: 'ChatList',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-options${focused ? '' : '-outline'}` :
'md-options'}
/>
),
};
ChatListScreen (Where the navigation starts from)
export default class ChatListScreen extends Component {
constructor(props) {
super(props);
}
static navigationOptions = {
title: "Chats"
};
renderRow({ item }) {
return (
<TouchableHighlight
onPress={() => this.props.navigation.navigate("ChatView")}
>
<ListItem
roundAvatar
title={item.name}
subtitle={item.subtitle}
avatar={{ uri: item.avatar_url }}
/>
</TouchableHighlight>
);
}
goToChat() {}
render() {
return (
<View style={styles.mainContainer}>
<SearchBar
lightTheme
icon={{ type: "font-awesome", name: "search" }}
placeholder="Type Here..."
/>
<List style={styles.listContainerStyle}>
<FlatList
data={users}
renderItem={this.renderRow}
keyExtractor={item => item.name}
/>
</List>
</View>
);
}
}
Chat(This is the target Chat screen)
export default class ChatScreen extends Component {
constructor() {
super();
this.state = {
messages: []
};
}
componentWillMount() {
this.setState({
messages: [
{
_id: 1,
text: "Hello test",
createdAt: new Date(),
user: {
_id: 2,
name: "dude"
}
}
]
});
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: 1
}}
/>
);
}
}
to solve this problem you must make a function to handle navigation then bind it in the contractor then send it in your Touchableopacity
Here is my I tried a but not able to resolve this simple one
export default class LoginScene extends Component {
constructor(props) {
super(props);
this.state = {
userid: '',
password: '',
};
}
componentWillMount() {
// this.fetchData();
}
veryfyLogin() {
fetch(LOGIN_URL)
.then((response) => {
if (response.status === 200) {
var navigator = this.props.navigator;
navigator.push({
id: 'dashboard',
title: 'DashBoardScene',
index: 1
});
}
Alert.alert(response.status);
})
.done();
}
render() {
onButtonPress = () => {
Alert.alert("userid:::"+this.state.userid+"Password:::"+this.state.password);
this.veryfyLogin();
};
return (
<View >
<Text> User Name: </Text>
<TextInput
style={{ height: 40 }}
ref= {(el) => { this.userid = el; }}
placeholder="enter your email address"
onChangeText={(userid) => this.setState({ userid })}
value={this.state.userid}
/>
<Text> Password: </Text>
<TextInput
ref= {(el) => { this.password = el; }}
style={{ height: 40 }} password={true}
placeholder="password"
onChangeText={(password) => this.setState({ password })}
value={this.state.password}
/>
<Button
title="Login"
color="#841584"
onPress={onButtonPress}
accessibilityLabel="Learn more about purple"
/>
</View>
)
}
}
LoginScene.propTypes = {
title: PropTypes.string.isRequired,
// onForward: PropTypes.func.isRequired,
// onBack: PropTypes.func.isRequired,
};
AppRegistry.registerComponent('LoginScene', () => LoginScene);
There is small mistake in the code
veryfyLogin() {
fetch(LOGIN_URL)
.then((response) => {
if (response.status === 200) {
var navigator = this.props.navigator;
navigator.push({
id: 'dashboard',
title: 'DashBoardScene',
index: 1
});
}
Alert.alert(response.status);
})
.done();
}
The Alert.alert(response.status); expecting an String but here the response.status is an integer after adding Alert.alert(response.status+"); it worked for me.
I need to call SearchScreen class method from a React Navigation Header.
The Navigator look like this:
Search: {
screen: SearchScreen,
path: 'search/:query',
navigationOptions: {
title: 'Search',
header: {
right: (
<MaterialCommunityIcons
name="filter"
onPress={() => {
console.log(this);
}}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
},
}
}
I've made it work by doing:
// declare static navigationOptions in the Component
static navigationOptions = {
title: 'Title',
header: ({ state }) => ({
right: (
<MaterialCommunityIcons
name="filter"
onPress={state.params.handleFilterPress}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
}),
}
_handleFilterPress() {
// do something
}
componentDidMount() {
// set handler method with setParams
this.props.navigation.setParams({
handleFilterPress: this._handleFilterPress.bind(this)
});
}
I've resolved the issue the following way:
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (
<TouchableOpacity
onPress={navigation.getParam('onPressSyncButton')}>
<Text>Sync</Text>
</TouchableOpacity>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ onPressSyncButton: this._onPressSyncButton });
}
_onPressSyncButton = () => {
console.log("function called");
}
Hooks solution with FunctionComponent, useState and useEffect
Ref the official docs (https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component) it is done by:
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button
onPress={navigation.getParam('increaseCount')}
title="+1"
color="#fff"
/>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ increaseCount: this._increaseCount });
}
state = {
count: 0,
};
_increaseCount = () => {
this.setState({ count: this.state.count + 1 });
};
/* later in the render function we display the count */
}
However I could not get this to work when working with the hooks api. My state variables were always undefined, but after I thought about how the hooks api is implemented it all made sense, so the solution was to update the navigation param every time a significant state variable was changed:
const [count, setCount] = useState(0);
useEffect(() => {
props.navigation.setParams({ increaseCount });
}, [count]);
const increaseCount = () => setCount(count + 1);
I came across same issue and able to resolve the issue from below links.
class MyScreen extends React.Component {
static navigationOptions = {
header: {
right: <Button title={"Save"} onPress={() => this.saveDetails()} />
}
};
saveDetails() {
alert('Save Details');
}
render() {
return (
<View />
);
}
}
Source: react-native issues 145
Below is my code
import React, { Component } from "react";
import {
Container,
Header,
Item,
Input,
Icon,
Button,
Text,
Left,
Body,
Right,
Content,
Spinner,
List,
ListItem
} from "native-base";
import { View, Image, StyleSheet, Keyboard } from "react-native";
import { connect } from "react-redux";
import {
onClear,
onSearchTextChanged,
searchForProfiles
} from "../../actions/searchActions";
class SearchBar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Header searchBar rounded>
<Button
iconLeft
style={{ paddingLeft: 0 }}
light
onPress={this.props.onBackPress}
>
<Icon style={{ marginLeft: 0, fontSize: 35 }} name="arrow-back" />
</Button>
<Item>
<Icon name="ios-search" />
<Input
placeholder="Search"
onChangeText={this.props.onChangeText}
value={this.props.searchText}
/>
<Button small transparent onPress={this.props.onClear}>
<Icon name="ios-close" />
</Button>
</Item>
<Button transparent onPress={this.props.onSearch}>
<Text>Search</Text>
</Button>
</Header>
);
}
}
class SearchWorld extends Component {
static navigationOptions = ({ navigation }) => ({
left: null,
header: () => {
const { state } = navigation;
return (
<SearchBar
onBackPress={() => navigation.goBack()}
onChangeText={text => {
state.params.onChangeText(text);
}}
onSearch={state.params.onSearch}
onClear={state.params.onClear}
searchText={state.params.searchText}
/>
);
}
});
onChangeText = text => {
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: text
});
this.props.onSearchTextChanged(text);
};
onSearch = () => {
Keyboard.dismiss();
const profile = { search: "test" };
const token = this.props.token;
this.props.searchForProfiles(token, profile);
};
onClear = () => {
this.props.onClear();
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: ""
});
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.onChangeText,
onSearch: this.onSearch,
onClear: this.onClear,
searchText: this.props.searchText
});
}
render() {
const { searchResults } = this.props;
let items = [];
if(searchResults && searchResults.data && searchResults.data.length > 0) {
items = [...searchResults.data];
}
return this.props.loading ? (
<Container style={{ alignItems: "center", justifyContent: "center" }}>
<Spinner color="#FE6320" />
</Container>
) : (
<Container>
<Content>
<List
style={{}}
dataArray={items}
renderRow={item => (
<ListItem style={{ marginLeft: 0}}>
<Text style={{paddingLeft: 10}}>{item.password}</Text>
</ListItem>
)}
/>
</Content>
</Container>
);
}
}
const mapStateToProps = state => {
const { token } = state.user;
const { searchText, searchResults, error, loading } = state.searchReaducer;
return {
token,
searchText,
searchResults,
error,
loading
};
};
export default connect(mapStateToProps, {
onClear,
onSearchTextChanged,
searchForProfiles
})(SearchWorld);
static navigationOptions = ({navigation}) => {
return {
headerTitle: () => <HeaderTitle />,
headerRight: () => (<Button iconLeft transparent small onPress = {navigation.getParam('onPressSyncButton')}>
<Icon style ={{color:'white', fontWeight:'bold'}} name='md-save' size = {32} />
<Text style ={{color:'white', fontWeight:'bold'}}>save</Text>
</Button>),
headerTintColor:'black',
headerStyle: {
backgroundColor: '#6200EE'
},
}
};
this.props.navigation.setParams({ onPressSyncButton: this.updateUserProfile });