I am using FlatList to write an infinite scroll, but it keeps sending request to my server forever. please see the code blow. I don't find any article clarify when the next page will load, what exactly does the onEndReached will be triggered.
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator, AsyncStorage } from 'react-native';
import { connect } from 'react-redux';
import { loadOrders } from '../redux/modules/Order';
import OrderListItem from './OrderListItem';
import { forOwn, isEmpty, reduce } from 'lodash';
class OrderList extends Component {
constructor(props) {
super(props);
this.state = {
page: 1,
error: null,
};
}
componentDidMount() {
this.loadOrders();
}
loadOrders = () => {
const { page } = this.state;
AsyncStorage.getItem("userToken")
.then((value) => {
return `Bearer ${value}`;
})
.then((userToken) => {
return this.props.loadOrders(page, { Authorization: userToken });
})
.then((response) => {
this.setState({
error: response.error || null,
});
})
.catch(error => {
this.setState({ error});
})
;
}
handleLoadMore = () => {
this.loadOrders();
};
onPressItem = (id: string) => {
};
keyExtractor = (item, index) => `order-item-${item.id}`;
renderItem = ({item}) => (
<OrderListItem
order={item}
onPressItem={this.onPressItem}
/>
);
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderFooter = () => {
if (!this.props.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
const { orders} = this.props;
if (orders.length> 0) {
return (
<View containerStyle={styles.container} >
<FlatList
data={orders}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
ListFooterComponent={this.renderFooter}
ItemSeparatorComponent={this.renderSeparator}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
</View>
);
}
return <View>
<Text>empty</Text>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
borderTopWidth: 0,
borderBottomWidth: 0
},
item: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#ccc'
}
});
const mapStateToProps = state => {
let order = state.get('order').toJS();
return {
orders: isEmpty(order.entities) ? [] : reduce(order.entities, (result, value) => {
result.push({ key: value.id, ...value });
return result;
}, []),
loading: order.loading
};
};
const mapDispatchToProps = {
loadOrders
};
export default connect(mapStateToProps, mapDispatchToProps)(OrderList);
the if part is false , but the onEndReached methods is still called, I must be insane.
the
Change this
onEndReachedThreshold={0.5}
to this:
onEndReachedThreshold={0}
Right now you're calling the end reached when you're halfway through. You can also try adding this to the FlatList:
legacyImplementation = {true}
If this still won't work I would recommend doing the 'pull' onRefresh. A nice example for you: https://www.youtube.com/watch?v=pHLFJs7jlI4
i met the problem too, in my case:
renderFooter somethings render null(height: 0) when loaded, but render ActivityIndicator when loading, and ActivityIndicator has its heigth bigger than 0(null's height)
when heigth change from 0 to ActivityIndicator's height, it will call onEndReached again
and you say the if part is false, i think its because it's not really false。
when code really run in FlatList, the if part is true, so it call onEndReached, and then the _scrollMetrics.contentLength or this._sentEndForContentLength has changed for some reason before your console in chrome. which makes the if part return false
above is all my thought for now, and i am still debugging for this problem, hope this answer will help you all
Related
I am developing React Native app, where must be overlay view before starting. Like "welcome screen". After you have read content in overlay view, you press ok.
setState is not working. Throw error TypeError: _this.setState is not a function
I'll tried to use setState like this:
removeElement = () => {
//this.state.loadingScreen = false;
this.setState({
loadingScreen: false
})
console.log(`Staten arvo nyt: ` + this.state.loadingScreen);
When I use setState it should re-render component, but I don't know what is the reason why it not works. I can change state this.state.loadingScreen = false;, but it is not re-rendering. And forceUpdate() not working either.
QUESTION: How can I render component again to get rid off overlay view?
My code:
import React, { useState, setState, Component } from "react";
import { Text, View, StyleSheet, TextInput, Button, Alert, TouchableOpacity} from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
state = {
loadingScreen: true,
}
let userInput = "";
let userInputName = "";
readUserInput = (text) => {
userInput = text
}
readUserInputName = (text) => {
userInputName = text
}
checkUserInput = () => {
if(userInput.length < 1 && userInputName.length < 1){
Alert.alert("Tarkista rekisterinumero ja nimi")
}
else{
storeData = async () => {
try{
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
}
catch(e){
console.log(e);
}
}
storeData();
navigation.navigate("GeneralInspection")
}
}
renderElement = () =>{
if(this.state.loadingScreen == true)
return <View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{fontSize:20}}>Change state to false</Text>
</TouchableOpacity>
</View>;;
return null;
}
removeElement = () => {
this.state.loadingScreen = false
}
setTimeout(
function() {
}
.bind(this),
1000
);
return (
<View style={styles.view}>
{ this.renderElement() }
<Text style={styles.text}>Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{fontSize:20}}>Aloita</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
view: {
height: "100%",
width: "100%",
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 40,
fontWeight: "bold",
padding:10,
fontStyle:"italic"
},
inputStyle: {
height:50,
borderColor:"black",
borderWidth:1,
width:"50%",
marginBottom:15,
textAlign:"center",
fontSize: 20,
borderRadius:5,
},
button: {
backgroundColor:"#007bff",
borderRadius:5,
padding:8,
width:"50%",
height: 60,
alignItems:"center",
justifyContent: "center",
},
fullScreen: {
height: "100%",
width: "100%",
zIndex: 100,
position: "absolute",
backgroundColor: "red",
}
});
You are using a functional component in the OP, which requires you to use useState Hooks for managing your state.
If you were using a class component, you could use the setState approach. In both cases, React will take care of the re-rendering part.
If using class-based approach, mutating state like this.state.loadingScreen = false will not trigger a re-render of the UI. You will have to use the setState method instead.
Class-based approach
import React from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default class StartPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loadingScreen: true,
userInput: "",
userInputName: ""
};
}
readUserInput = (text) => {
this.setState({
userInput: text
});
}
readUserInputName = (text) => {
this.setState({
userInputName: text
});
}
storeData = async () => {
const { userInput, userInputName } = this.state;
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
this.props.navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
}
checkUserInput = () => {
const { userInput, userInputName } = this.state;
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
this.storeData();
}
renderElement = () => {
if (this.state.loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
}
removeElement = () => {
this.setState({
loadingScreen: false
});
}
render() {
return (
<View style={styles.view} >
{ this.renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={this.state.userInputName}
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={this.state.userInput}
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
}
Functional component-based approach
import React, { useState } from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
const [loadingScreen, setLoadingScreen] = useState(true);
const [userInput, setUserInput] = useState('');
const [userInputName, setUserInputName] = useState('');
const readUserInput = (text) => {
setUserInput(text);
};
const readUserInputName = (text) => {
setUserInputName(text);
};
const storeData = async () => {
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
};
const checkUserInput = () => {
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
storeData();
};
const renderElement = () => {
if (loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
};
const removeElement = () => {
setLoadingScreen(false);
};
return (
<View style={styles.view} >
{renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={userInputName}
onChangeText={readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={userInput}
onChangeText={readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
I am new to react native. I am doing a simple app where I add name and age of a person to firebase and then showing it in the list, I am using flatList in this project but it asks to import all the attributes of the flatList. if I add only 2 attributes like data, renderItem it gives an error, please help
here my code
import React from "react";
import {StyleSheet, View, Button, Text, FlatList, TextInput, ListView} from "react-native";
import firebase from './firebase'
let db = firebase.firestore();
class TextInputExample extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
userName: '',
userAge: '',
input1Background: 'red',
textColor1: 'white',
input2Background: 'red',
textColor2: 'white'
};
}
componentDidMount(): void {
db.collection('users')
.onSnapshot((snapshot) => {
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
this.state.data.push({
name: change.doc.get('name'),
age: change.doc.get('age')
});
console.log(this.state.data);
}
})
}, (error => {
console.log(error.message);
}))
}
addToDatabase = () => {
let data = {
name: this.state.userName,
age: this.state.userAge
};
console.log(data);
db.collection('users').add(data)
.then(ref => {
}).catch(msg => {
console.log(msg);
});
};
renderItem = ({item}) => {
return(
<View>
<Text>{item.name}</Text>
<Text>{item.age}</Text>
</View>
);
};
render(): React.Node {
return (
<View style={styles.container}>
<TextInput
placeHolder={'Enter name'}
onChangeText={(text) => this.setState( {userName: text} )}
/>
<TextInput
placeHolder={'Enter Age'}
onChangeText={(text) => this.setState( {userAge: text} )}
/>
<Button title={'Add'} onPress={() => this.addToDatabase()}/>
<View>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
/>
</View>
</View>
);
}
}
export default TextInputExample;
const styles = StyleSheet.create({
container: {
flex: 1, alignSelf: 'center', alignItems: 'center'
}
});
I think your error is because you're updating your state in the wrong way, if you want to add an element to an array in your state you must do it using the setState method and not directly accessing to the array and push it.
Do this
const newItem = {
name: change.doc.get('name'),
age: change.doc.get('age')
}
this.setState((prevState) => ({
...prevState,
data: [...prevState, newItem]
}))
Using Expo SDK 29 for a react native application.
I would like to use a flat list component. This makes up the entirety of a SafeAreaView component. I make this point as there are lots of issues relating to a flat list inside of a scroll view which this is not.
The flat list shows a list of jobs.
I have added a jobLoading boolean to the redux state to manage when the list should show as refreshing and can confirm that this toggles as expected when firing the actions to fetch the data and the success.
When i add the props to the flat list for onRefresh and refreshing the component seems to work by showing the activity indicator in the UI but does not fire the onRefresh function. I have tried implementing the call in numerous ways but nothing happens. The result is that the activity indicator shows itself and never disappears.
As it's Expo SDK 29 the React Native version is 0.55.4
Anyone have any ideas of what to try. I've spent a couple of hours looking at this trying various things but suggestions are welcome.
Thanks in advance.
EDIT: Added the code for reference. Reducer for refreshing sets true when fetchJobs() is dispatched and false when a success or error is recieved. The console log for onRefresh never triggers.
import * as React from 'react'
import * as actions from '../../redux/actions'
import { ActivityIndicator, FlatList, KeyboardAvoidingView, Dimensions, SafeAreaView, StyleSheet, View } from 'react-native'
import { ApplicationState, JobState, Job } from '../../redux'
import { Button, Form, Input, Item, Text, Icon } from 'native-base'
import { JobListItem } from './jobListItem'
import { StateHandlerMap, compose, lifecycle, withPropsOnChange, withStateHandlers } from 'recompose'
import { connect } from 'react-redux'
interface ReduxStateProps {
jobs: JobState
refreshing: boolean
screenOrientation: string
}
interface ReduxDispatchProps {
fetchJobs: (param?: string) => any
}
export interface DataItem {
key: string
data: Job
}
interface ListProps {
jobList: DataItem[]
}
interface SearchStateProps {
timer: number | undefined
searchString: string
}
interface SearchHandlerProps extends StateHandlerMap<SearchStateProps> {
updateSearch: (searchString: string) => any
setTimer: (timer: number | undefined) => any
}
type OuterProps = {}
type InnerProps = OuterProps & ReduxStateProps & ReduxDispatchProps & ListProps & SearchStateProps & SearchHandlerProps
const enhance = compose<InnerProps, OuterProps>(
connect<ReduxStateProps, ReduxDispatchProps, OuterProps, ApplicationState>(
state => ({
jobs: state.job,
refreshing: state.jobLoading,
screenOrientation: state.screenOrientation
}),
dispatch => ({
fetchJobs: (param?: string) => dispatch(actions.jobs.request({ param }))
})
),
withPropsOnChange<ListProps, OuterProps & ReduxStateProps & ReduxDispatchProps>(
['jobs', 'screenOrientation'],
props => ({
jobList: props.jobs && Object.keys(props.jobs).map(job => ({ key: job, data: props.jobs[Number(job)] }))
})
),
withStateHandlers<SearchStateProps, SearchHandlerProps, OuterProps>(
{
timer: undefined,
searchString: ''
},
{
updateSearch: state => (searchString: string) => ({ searchString }),
setTimer: state => (timer: number | undefined) => ({ timer })
}
),
lifecycle<InnerProps, {}>({
componentDidMount() {
this.props.fetchJobs()
}
})
)
export const JobList = enhance(({ fetchJobs, jobList, refreshing, screenOrientation, searchString, setTimer, timer, updateSearch }) => {
const onSearchChange = (search: string) => {
clearTimeout(timer)
updateSearch(search)
const timing = setTimeout(() => {
fetchJobs(search)
}, 500)
setTimer(timing)
}
const onRefresh = () => {
console.log('requesting refresh')
fetchJobs()
}
return (
<SafeAreaView style={{ flex: 1}}>
<KeyboardAvoidingView style={{ flexDirection: 'row', justifyContent: 'space-evenly', paddingTop: 3, paddingRight: 3 }}>
<Form style={{ flex: 1, paddingLeft: 10, paddingRight: 10 }}>
<Item>
<Input
value={searchString}
onChangeText={(text: string) => onSearchChange(text)}
placeholder='Search'
/>
</Item>
</Form>
<Button onPress={() => {fetchJobs(); updateSearch('')}}>
<Icon name='refresh' />
</Button>
</KeyboardAvoidingView>
{refreshing &&
<View style={styles.refreshContainer}>
<Text style={{ paddingBottom: 10 }}>Fetching Data</Text>
<ActivityIndicator />
</View>
}
<FlatList
keyExtractor={item => item.key}
data={jobList}
renderItem={({ item }) =>
<JobListItem
screenOrientation={screenOrientation}
item={item}
/>
}
onRefresh={onRefresh}
refreshing={refreshing}
/>
</SafeAreaView>
)
})
const styles = StyleSheet.create({
refreshContainer: {
height: 60,
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
})
I'm having the exact same issue and I'm using expo SDK 30. But my case is a little bit different. The onRefresh function is called everytime I pull, however if I scroll down my list, and scroll back up fast, the loading indicator shows up, but my onRefresh function is not called.
My refreshing prop is set on my reducer, and my onRefresh function dispatches an action that fetches data and set refreshing true and false.
Here is my code:
class NoticiasScreen extends Component {
static navigationOptions = {
header: <Header
title='Notícias Alego'
leftComponent={<Image source={require('../../../assets/images/play_grande.png')} style={imageStyle} resizeMode='contain'/>}
/>
}
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
this.keyExtractor = this.keyExtractor.bind(this);
this.renderContent = this.renderContent.bind(this);
this.navigateToNoticias = this.navigateToNoticias.bind(this);
this.carregarMaisNoticias = this.carregarMaisNoticias.bind(this);
this.onRefresh = this.onRefresh.bind(this);
}
componentDidMount() {
this.props.carregarNoticias(this.props.pagina);
}
renderItem({item}) {
return (
<NoticiaListItem noticia={item} abrirNoticia={this.navigateToNoticias} />
);
}
keyExtractor(item) {
return item.id.toString();
}
navigateToNoticias(noticia) {
this.props.navigation.navigate('NoticiasExibir', { id: noticia.id });
}
onRefresh() {
console.log('onRfresh');
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
}
renderContent() {
const { noticias, carregandoNoticias, erroNoticias } = this.props;
if(noticias.length === 0 && carregandoNoticias) {
return (
<View style={styles.containerCenter}>
<ActivityIndicator size="large" color={colors.verde}/>
</View>
);
}
if(erroNoticias) {
return (
<View style={styles.containerCenter}>
<Text style={styles.message}>{erroNoticias}</Text>
<TouchableOpacity hitSlop={hitSlop15}>
<Text>Recarregar</Text>
</TouchableOpacity>
</View>
)
}
return (
[<TextInput
style={styles.textInput}
placeholder='Pesquise'
key='pesquisa'
underlineColorAndroid='transparent'
/>,
<FlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>]
)
}
render() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
{this.renderContent()}
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return {
noticias: state.intranet.noticias,
pagina: state.intranet.pagina,
erroNoticias: state.intranet.erroNoticias,
carregandoNoticias: state.intranet.carregandoNoticias
}
}
function mapDispatchToProps(dispatch) {
return {
carregarNoticias: (pagina, recarregar) => dispatch(ActionCreator.carregarNoticias(pagina, recarregar))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NoticiasScreen);
No idea what's going on. Any help is appreciated.
EDIT:
I fixed it somehow. I added the onMomentScrollBegin prop to prevent my flatList from rendering twice on Render, and that fixed this issue.
here is what I added:
constructor(props) {
super(props);
...
this.onRefresh = this.onRefresh.bind(this);
this.onMomentumScrollBegin = this.onMomentumScrollBegin.bind(this);
this.onEndReachedCalledDuringMomentum = true; //PUT THIS HERE
}
onRefresh() {
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
if(!this.onEndReachedCalledDuringMomentum){
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
this.onEndReachedCalledDuringMomentum = true;
}
}
onMomentumScrollBegin() {
this.onEndReachedCalledDuringMomentum = false;
}
render() {
<OptimizedFlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onMomentumScrollBegin={this.onMomentumScrollBegin} //PUT THIS HERE
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>
}
My situation is i have two FlatList component (A and B).
A is my first screen. I use react-navigation to navigate A to B , B will show back arrow on headerLeft. When i click the arrow it will back to A . But the FlatList data is still show B even it is really in A...
My data is from fetch API by react-redux, i think the problem is come from react-redux. Because i test a simple test without react-redux. The problem is gone.
I want to use react-redux create my project. I try to use shouldComponentUpdate like
shouldComponentUpdate = (nextProps, nextState) => {
if (nextProps.movieList === this.props.movieList) {
return false;
}
return true;
};
It is still can't fix my problem when goBack() to another component
I console.log it try to find what is going on with my props data.
When i navigate to B from A. My console.log will show like this, i find A component will be rendered...
Then i click the back arrow on headerLeft to A. The screen is A but the data is still B add my console.log is empty at the same time.
I can't figure it out. Any help would be appreciated. Thanks in advance.
Here is my A component file (B is similar with A):
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
TouchableOpacity, Image,
ActivityIndicator, Alert, Platform
} from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { fetchMainMovieList } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class MainActivity extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'MainActivity',
headerLeft:
<TouchableOpacity style={{ marginLeft: 10 }} onPress={() => navigation.navigate('DrawerOpen')} >
<Icon name='menu' />
</TouchableOpacity>
});
componentWillMount() {
this.props.fetchMainMovieList();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 220, width: equalWidth }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('A component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={2}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchMainMovieList })(MainActivity);
Here is my B component file:
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
Image, ActivityIndicator, Text
} from 'react-native';
import { connect } from 'react-redux';
import { fetchThisWeek } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class ThisWeek extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'ThisWeek',
});
componentWillMount() {
this.props.fetchThisWeek();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 500, width: '100%' }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('B component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={1}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchThisWeek })(ThisWeek);
Here is my MyListReducer.js:
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return action.payload;
case MOVIELIST_THISWEEK:
return action.payload;
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return state;
}
};
In your reducer you have added the fetched data into the main object in store, instead, you should have to maintain two different variables to save data of those different components separately. Try by changing the reducer as,
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {
weeklyMovies:[],
allMovies:[]
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return {
...state,
allMovies:action.payload
};
case MOVIELIST_THISWEEK:
return {
...state,
weeklyMovies:action.payload
};
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return {...state};
}
};
And in your component A and B you should change your mapStateToProps to read data from corresponding objects in store.
For MainActivity component
const mapStateToProps = (state) => {
const movieList = state.allMovies;
return { movieList };
};
and for ThisWeek component
const mapStateToProps = (state) => {
const movieList = state.weeklyMovies;
return { movieList };
};
I am building an App and referring this link
i implemented same code for my App, but i am getting error "Cannot read the property 'getSelectedItemsExt' of undefined".
One more error is "submit" button is also not showing up. I have tried all the ways but failed.
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, TextInput, View, ListView, Alert, Button, Platform, ToastAndroid, TouchableOpacity, ActivityIndicator, Text, Picker, ScrollView }
from 'react-native';
import { StackNavigator } from 'react-navigation';
import MultiSelect from 'react-native-multiple-select';
class manage_publishers extends Component {
static navigationOptions = {
title: 'Manage Publishers',
};
constructor() {
super()
this.state = {
isLoading: true,
selectedPublishers1:[],
publishersByCategory: [],
publishersByClient: [],
publishersByGroup: [],
dataSource:[]
}
}
componentDidMount()
{
const base64 = require('base-64');
fetch('APIURL'+this.props.navigation.state.params.id,
{
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
"Authorization": "Basic " + base64.encode("ABC:XYZ")
}
}).then((response) => response.json()
)
.then((responseJson) => {
this.setState({
categories: responseJson.PublisherByCategory,
}, function () {
});
})
.catch((error) => {
console.log("error in category");
console.log(error);
});
}
onSelectedPublishersByCategoryChange = (publishersByCategory) => {
console.log(publishersByCategory);
this.setState({ publishersByCategory });
}
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<ActivityIndicator />
</View>
);
}
return ([
<View style={{flex: 1,paddingTop: (Platform.OS === 'ios') ? 20 : 20, padding: 5}}>
<Text style={{ padding: 5, fontSize: 35, backgroundColor: '#2196F3', marginBottom: 7 }}>
Manage Publishers
</Text>
<MultiSelect
items={this.state.categories}
uniqueKey="id"
ref={(component) => { this.multiSelect = component }}
onSelectedItemsChange={this.onSelectedPublishersByCategoryChange}
selectedItems={this.state.publishersByCategory}
selectText="Publishers by Category"
searchInputPlaceholderText="Search Publisher..."
onChangeInput={ (text)=> console.log(text)}
altFontFamily="ProximaNova-Light"
tagRemoveIconColor="#CCC"
tagBorderColor="#CCC"
tagTextColor="#CCC"
selectedItemTextColor="#CCC"
selectedItemIconColor="#CCC"
itemTextColor="#000"
displayKey="name"
searchInputStyle={{ color: '#CCC' }}
submitButtonColor="#CCC"
submitButtonText="Submit"
/>
</View>,
<View>
{this.multiSelect.getSelectedItemsExt(selectedItems)}
</View>
]);
}
}
});
module.exports = manage_publishers;
Please have a look at this and provide me solution, I'll be very thankful .
I had that same issue, and I solved adding a AND condition:
{this.multiSelect && this.multiSelect.getSelectedItemsExt(selectedItems)}
If you are using functional components you can do like this,
create ref like this,
const multiSelect = useRef(null)
Access the getSelectedItemsExt function like this,
<View>
{multiSelect.current && multiSelect.current.getSelectedItemsExt &&
multiSelect.current.getSelectedItemsExt(countries)}
</View>
It happened because you called a method before the reference has been set.
Use this code:
<View>
{ this.multiSelect ? this.multiSelect.getSelectedItemsExt(selectedItems) : null}
</View>
Reference to this issue:
https://github.com/toystars/react-native-multiple-select/issues/58#issuecomment-364136438