React (react-native-modalize) not working with flatListProps object - reactjs

Is anyone using react-native-modalize module?
react-native-modalize module, when i render code on flatListProps object its showing the error below!!
Here is the example as well https://jeremybarbet.github.io/react-native-modalize/#/EXAMPLES
import React, { useRef } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { Modalize } from 'react-native-modalize';
export const App = () => {
const modalizeRef = useRef<Modalize>(null);
const onOpen = () => {
modalizeRef.current?.open();
};
const arrayData = [ { "heading": "test"}, {"heading": "test2"}... ]
let data = { "object": [arrayData] }
const getData = () => ({ data });
const renderItem = (item) => (
<View>
<Text>{item.heading}</Text>
</View>
);
return (
<>
<TouchableOpacity onPress={onOpen}>
<Text>Open the modal</Text>
</TouchableOpacity>
<Modalize
ref={modalizeRef}
flatListProps={{
data: getData(),
renderItem: renderItem,
keyExtractor: item => item.heading,
showsVerticalScrollIndicator: false,
}}
/>
</>
);
}

data of flatListProps is an array object.
let data = { "object": [arrayData] }
const getData = () => ([ ...data.object ]);

Related

My SetState is erasing my previous content

I return this content from my ReviewPictures.tsx screen.
My problem is when i take a new Photo with my Photo Button
My SetState erase the previous Photos List
The Code :
ReviewPictures.tsx : Here i return my photos and my PhotoIcons Button
which can retake a photo if the user want so.
import {NavigationProp, RouteProp, useNavigation, useRoute} from '#react-navigation/native'; import React, {useContext, useEffect, useState} from 'react'; import {Image, StyleSheet, Text, View, ScrollView} from 'react-native'; import {Button} from 'react-native-paper'; import AnalyseContext from '../../contexts/Photoshoot/AnalyseContext'; import {Step, StepAlias} from '../../domain/photoShoot/Step'; import {PhotoshootStackParamList} from '../../navigation/PhotoshootNavigator'; import {IconButton, Colors} from 'react-native-paper'; import I18n from 'i18n-js'; import {AppStackParamList} from '../../navigation/AppNavigator'; import {OnboardedStackParamList} from '../../navigation/OnboardedNavigator';
const styles = StyleSheet.create({ image: {
width: 150,
height: 150, }, container: {
flex: 1, }, box: {height: 250}, row: {flexDirection: 'column', alignItems: 'center'}, });
const ReviewPictures: React.FC = () => {
const {params} = useRoute<RouteProp<OnboardedStackParamList, 'Photoshoot'>>();
const {picturePaths} = useContext(AnalyseContext);
const [content, setContent] = useState<JSX.Element[]>([]);
const navigation = useNavigation<NavigationProp<PhotoshootStackParamList>>();
const Appnavigation = useNavigation<NavigationProp<AppStackParamList>>();
const toRedo = !params?.redo;
useEffect(() => {
setContent(setPageContent());
}, []);
const setPageContent = () => {
const c: JSX.Element[] = [];
picturePaths.forEach((value: string, step: StepAlias) => {
console.log(value);
c.push(
<View style={[styles.box, styles.row]}>
<Text>{I18n.t(`${step}`)}</Text>
<Image style={styles.image} source={{uri: value}} />
<IconButton
icon="camera"
color={Colors.blue500}
size={40}
onPress={() => {
console.log('Pressed');
Appnavigation.navigate('Logged', {
screen: 'Onboarded',
params: {screen: 'Photoshoot', params: {redo: toRedo, onboarded: false, review: step}},
});
}}
/>
</View>,
);
});
return c; };
return (
<>
<ScrollView style={styles.container}>{content}</ScrollView>
<Button onPress={() => navigation.navigate('Mileage')}>Valider</Button>
</> ); };
export default ReviewPictures;
And Photoshoot.tsx which take and store the photos :
import {RouteProp, useRoute} from '#react-navigation/native';
import I18n from 'i18n-js';
import React, {useContext, useEffect, useState} from 'react';
import {StatusBar} from 'react-native';
import Loading from '../../components/UI/loading/Loading';
import AnalysesContext from '../../contexts/Analyses/AnalysesContext';
import {UserContext} from '../../contexts/User/UserContext';
import {ONBOARDING_SCENARIO, WEAR_SCENARIO} from '../../domain/photoShoot/Scenario';
import {fromStepAlias, Step} from '../../domain/photoShoot/Step';
import {OnboardedStackParamList} from '../../navigation/OnboardedNavigator';
import PhotoshootNavigator from '../../navigation/PhotoshootNavigator';
import {
createRetakeScenario,
extractReferenceTyresToRetake,
extractWearTyresToRetake,
} from '../../utils/retakeTyres.utils';
/**
* Wrap AnalyseWearNavigator.
* Ensures steps and/or referenceId are loaded before use of AnalyseWearNavigator.
* Meanwhile, it shows a waiting loader.
*
* We have to wait before mounting AnalyseWearNavigator. Otherwise, Navigator take configuration it had at first mount and don't care if you update state later.
*/
const Photoshoot = () => {
const {params} = useRoute<RouteProp<OnboardedStackParamList, 'Photoshoot'>>();
const {user, vehicleId} = useContext(UserContext);
const {wear, reference, fetchingAnalysis} = useContext(AnalysesContext);
const [isLoading, setIsLoading] = useState(true);
const [referenceId, setReferenceId] = useState<number>();
const [steps, setSteps] = useState<Step[]>([]);
useEffect(() => {
if (!fetchingAnalysis) {
if (user && vehicleId) {
if (params?.redo) {
loadRetakeScenario();
}
if (params?.review) {
console.log('Joué ' + params?.review);
loadReviewScenario();
} else {
loadScenario();
}
}
}
}, [user, vehicleId, wear, reference, params, fetchingAnalysis]);
const loadReviewScenario = () => {
if (params?.review) {
setSteps([fromStepAlias(params.review)]);
setIsLoading(false);
// HERE THE PROBLEM WITH SETSTEPS
}
};
const loadRetakeScenario = () => {
const wearTyresToRetake = extractWearTyresToRetake(wear?.tyreWears);
const referenceTyresToRetake = extractReferenceTyresToRetake(reference?.tyreReferences);
const scenario = createRetakeScenario(wearTyresToRetake, referenceTyresToRetake);
setSteps(scenario);
setIsLoading(false);
};
const loadScenario = async () => {
setReferenceId(reference?.id);
setSteps(!!params?.onboarded ? WEAR_SCENARIO : ONBOARDING_SCENARIO);
setIsLoading(false);
};
const content = isLoading ? (
<Loading waitingText={I18n.t('ANALYSIS_PAGE.LOADING_MESSAGE')} />
) : (
<>
<StatusBar barStyle="dark-content" />
<PhotoshootNavigator steps={steps} referenceId={referenceId} redo={!!params?.redo} />
</>
);
return content;
};
export default Photoshoot;
Step.ts which guarantee the position of the tyre, it's a Tyre Recognition app :
import {PhotoType} from '../../models/PhotoType';
import {TyrePosition} from '../TyrePosition';
/** Step of a photo shoot */
export type Step = {
tyre: TyrePosition;
whichSideToTake: PhotoType;
};
export type StepAlias = `${TyrePosition}_${PhotoType}`;
export const toStepAlias = (step: Step): StepAlias => {
return `${step.tyre}_${step.whichSideToTake}`;
};
export const fromStepAlias = (stepAlias: StepAlias): Step => {
console.log(stepAlias.split('_'));
const split = stepAlias.split('_');
return {tyre: split[0] as TyrePosition, whichSideToTake: split[1] as PhotoType};
};
What's wrong with setStep ?
From what I can understand of your post, you are having some issue with the step state and updating it. In the three places in Photoshoot.tsx file where you enqueue any steps state updates you should probably use a functional state update to shallowly copy and update from any previously existing state instead of fully replacing it.
Example:
const loadReviewScenario = () => {
if (params?.review) {
setSteps(steps => [...steps, fromStepAlias(params.review)]);
setIsLoading(false);
}
};
const loadRetakeScenario = () => {
const wearTyresToRetake = extractWearTyresToRetake(wear?.tyreWears);
const referenceTyresToRetake = extractReferenceTyresToRetake(reference?.tyreReferences);
const scenario = createRetakeScenario(wearTyresToRetake, referenceTyresToRetake);
setSteps(steps => [...steps, scenario]);
setIsLoading(false);
};
const loadScenario = async () => {
setReferenceId(reference?.id);
setSteps(steps => [
...steps,
!!params?.onboarded ? WEAR_SCENARIO : ONBOARDING_SCENARIO
]);
setIsLoading(false);
};

Jest - Mock - toHaveBeenCalled() not working as expected?

Not understanding why the Mock function is not showing the tohaveBeenCalled when i am trying to mock in below fashion
//my App.js
import useAuth from "./src/hooks/useAuth";
import useBrunch from "./src/hooks/useBrunch";
import { someFunct } from "./functional";
export default function App() {
const { funct } = useBrunch();
return (
<>
<Max funct={funct} />
</>
);
}
function Max() {
const LoginSchema = object().shape({
emailId: string(),
password: string(),
});
const { funct } = useBrunch();
// const { funct, sas } = useBrunch();
// const [flag, setFlag] = React.useState(false);
//const { someFunct } = useAuth();
return (
<Formik
initialValues={{ emailId: "some#gmail.com" }}
onSubmit={(a, v) => {
funct(a);
}}
validationSchema={LoginSchema}
>
{({ handleSubmit, handleBlur, values, dirty, touched, errors }) => {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar testID="someId" style="auto" />
<CheckBox />
<TouchableOpacity
testID="masa"
disabled={false}
onPress={handleSubmit}
>
<View>
<Text testID="hello">SOmething</Text>
</View>
</TouchableOpacity>
<Button testID="something" title="someddd" onPress={() => {}} />
</View>
);
}}
</Formik>
);
}
export { Max };
//My hooks/useBrunch.js method
const useBrunch = () => {
console.log("called");
const funct = (a) => {
console.log("funct executed");
};
return { funct };
};
export default useBrunch;
//My App.test.js file
import React from "react";
import { render, act, waitFor, fireEvent } from "#testing-library/react-native";
import useBrunch from "./src/hooks/useBrunch";
import { Max } from "./App";
jest.mock("./src/hooks/useBrunch", () =>
jest.fn(() => ({
funct: jest.fn((a) => {
console.log(a, "called");
return a;
}),
}))
);
describe("Login something", () => {
it("first clss", async () => {
let { getByTestId, toJSON } = render(<Max />);
const something = getByTestId("masa");
await waitFor(() => {
fireEvent.press(something);
});
expect(useBrunch().funct).toHaveBeenCalled();
});
});
Expected- on simulation button bres the expected output should have a success
i have console inside mock function getting called and also shows paramter,
but cannot register that under toHaveBeenCalled() or similiar jest methods
Can anybody helpout here please?!
Try giving something like this:
const mockFunct = jest.fn((a) => {
console.log(a, "called");
return a;
})
jest.mock("./src/hooks/useBrunch", () =>
jest.fn(() => ({
funct: mockFunct
})
));
and then
expect(mockFunct).toHaveBeenCalled();

RecyclerListView scrolls to top onEndReached with functional component

I have implemented a RecyclerListView(Flipkart Github) using Redux as seen below. Everything seems to be working great except when onEndReached is called and a new batch of data comes through, the list gets positioned to the top of the page rather than remaining smooth. See that behavior in the GIF below:
Note: This is happening on web(chrome). I tried the latest stable and 2.0.13-alpha.1
import React, { useCallback } from 'react';
import { View, Text, Dimensions } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { RecyclerListView, DataProvider, LayoutProvider } from 'recyclerlistview/web';
import { createSelector } from 'reselect';
import { loadData } from '../actions';
const selectData = createSelector(
state => state.data,
data => Object.values(data),
);
let containerCount = 0;
class CellContainer extends React.Component {
constructor(args) {
super(args);
this._containerId = containerCount++;
}
render() {
return (
<View {...this.props}>
{this.props.children}
<Text>Cell Id: {this._containerId}</Text>
</View>
);
}
}
const List = ({ isServer }) => {
let { width } = Dimensions.get('window');
const dispatch = useDispatch();
const data = useSelector(selectData);
const dataDataProvider = new DataProvider((r1, r2) => {
return r1 !== r2;
}).cloneWithRows(data);
const onEndReached = useCallback(() => dispatch(loadData()), [dispatch]);
const layoutProvider = new LayoutProvider(
() => 0,
(type, dim) => {
dim.width = width;
dim.height = 240;
},
);
const rowRenderer = (type, data) => {
return <CellContainer />;
};
return (
<View
style={{
display: "flex",
flex: 1,
width: "100vw",
height: "100vh"
}}
>
<RecyclerListView
layoutProvider={layoutProvider}
dataProvider={dataDataProvider}
onEndReached={onEndReached}
rowRenderer={rowRenderer}
/>
</View>
);
};
export default List;
UPDATE: I can confirm that the class-based version works with no issues using redux connect. Leaning towards this being some kind of incompatibility with the library. Interesting nonetheless.
Below snippet is a simplified working example of this demo https://codesandbox.io/s/k54j2zx977
class App extends React.Component {
constructor(props) {
let { width } = Dimensions.get('window');
super(props);
this.state = {
dataProvider: new DataProvider((r1, r2) => {
return r1 !== r2;
}),
layoutProvider: new LayoutProvider(
index => 0,
(type, dim) => {
dim.width = width;
dim.height = 240;
},
),
count: 0,
};
}
componentDidUpdate(prevProps) {
if (this.props.data.length !== prevProps.data.length) {
this.setState({
dataProvider: this.state.dataProvider.cloneWithRows(this.props.data),
count: this.props.data.length
});
}
}
componentWillMount() {
this.props.loadData();
}
async fetchMoreData() {
this.props.loadData();
}
rowRenderer = (type, data) => {
//We have only one view type so not checks are needed here
return <CellContainer />;
};
handleListEnd = () => {
this.fetchMoreData();
//This is necessary to ensure that activity indicator inside footer gets rendered. This is required given the implementation I have done in this sample
this.setState({});
};
render() {
//Only render RLV once you have the data
return (
<View style={styles.container}>
{this.state.count > 0 ? (
<RecyclerListView
style={{ flex: 1 }}
contentContainerStyle={{ margin: 3 }}
onEndReached={this.handleListEnd}
dataProvider={this.state.dataProvider}
layoutProvider={this.state.layoutProvider}
renderAheadOffset={0}
rowRenderer={this.rowRenderer}
/>
) : null}
</View>
);
}
}
export default connect(
state => ({
data: selectData(state),
}),
dispatch => bindActionCreators({ loadData }, dispatch),
)(App);
Just add the below line of code after your layoutProvider. The shouldRefreshWithAnchoring property of layoutProvider prevents recycler view list to not refresh the existing rows if more rows are added to the list.
layoutProvider.shouldRefreshWithAnchoring = false;
Solution is here:
https://github.com/Flipkart/recyclerlistview/issues/449
Just add
const [layoutProvider] = React.useState(
new LayoutProvider(
(index) => 1,
(type, dim) => {
dim.width = ITEM_HEIGHT
dim.height = ITEM_HEIGHT
}
)
)

Accessing Refs in React Functional Component

Im using this package to add credit cards to my app, how would you åccess the refs as in the example when using this in a functional component?
This is how they show to update values:
this.refs.CCInput.setValues({ number: "4242" });
I don't know how to access that inside a functional component?
This is my component to Edit a card, and I want to add the current values to the inputs.
import React, {useContext, useState, useRef} from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import {CreditCardInput} from 'react-native-input-credit-card';
import Store from '../../store/context';
const styles = StyleSheet.create({
});
export default function EditCard(props) {
const {navigate} = props.navigation;
const {cardNumber} = props.navigation.state.params;
const {state, dispatch} = useContext(Store);
const [card, setCard] = useState(
state.cards.find(card => {
return card.values.number === cardNumber;
}),
);
const _onChange = form => {
if (form.valid) {
setCard(form);
}
};
const updateCard = () => {
dispatch({
type: 'ADD_CARD',
payload: card,
});
navigate.goBack();
};
return (
<View style={styles.container}>
<CreditCardInput
validColor={'#47B278'}
invalidColor={'#E23C3C'}
placeholderColor={'#efefef'}
onChange={_onChange}
requiresName
/>
<TouchableOpacity
style={
card
? card.valid
? styles.button
: styles.buttonDisabled
: styles.buttonDisabled
}
disabled={card ? (card.valid ? false : true) : true}
onPress={updateCard}>
<Text style={styles.buttonText}>Update Credit Card</Text>
</TouchableOpacity>
</View>
);
}
You need to use useRef hook:
const cciRef = useRef();
<CreditCardInput ref={cciRef}/>;
// cciRef.current holds the reference
export default function EditCard(props) {
const cciRef = useRef();
useEffect(() => {
console.log(cciRef.current);
cciRef.current.setValues({ number: "4242" });
}, []);
return (
<View style={styles.container}>
<CreditCardInput ref={cciRef} />
</View>
);
}

React with Redux: Child component does not rerender after props have changed (even though they are not shallow equal)

I'm building an app with React Native using Redux for the state management. I will post my code for all the involved components and the reducer down below, but since that is going to be much, let me describe the problem in a few sentences first.
I have an immutable reducer for my objects called 'waitercalls'. I have a screen (HomeScreen) that renders two components. Each component is a <FlatList /> of objects. The objects (waitercalls) are given to them via props by it's parent (HomeScreen). HomeScreen is connected to Redux via React-Redux' connect() and gets the objects ('waitercalls') via a selector created with Re-Select.
The left list's items can be pressed and upon press dispatch an event to the reducer. Here comes the problem. When the left list's item are pressed, the left list correctly updates (calls render()). The right list though does not re-render, even though it gets the same props.
Why does the left list rerender, but the right list not? The reducer is immutable, the selector is too and even the length of the list changes from one to zero which should eliminate the possibility of a shallow equal.
And now for the code:
waitercallsReducer:
import { createSelector } from "reselect";
const initialState = {};
const waitercallsReducer = (state = initialState, action) => {
if (action.payload && action.payload.entities && action.payload.entities.waitercalls) {
return {
...state,
...action.payload.entities.waitercalls
};
} else {
return state;
}
};
export default waitercallsReducer;
export const getAllWaitercallsNormalizedSelector = state => state.waitercalls;
export const getAllWaitercallsSelector = createSelector(
getAllWaitercallsNormalizedSelector,
waitercalls => Object.values(waitercalls)
);
export const getAllActiveWaitercallsSelector = createSelector(
getAllWaitercallsSelector,
waitercalls => waitercalls.filter(waitercall => !waitercall.done)
);
Action creators:
import { setValues } from "../core/core";
// feature name
export const WAITERCALLS = "[Waitercalls]";
// action creators
export const setValues = (values, type) => ({
type: `SET ${type}`,
payload: values,
meta: { feature: type }
});
export const setWaitercalls = waitercalls => setValues(waitercalls, WAITERCALLS);
HomeScreen:
import React, { Component } from "react";
import { View, TouchableOpacity } from "react-native";
import { SafeAreaView } from "react-navigation";
import { connect } from "react-redux";
import { Icon } from "react-native-elements";
import PropTypes from "prop-types";
// ... I've omitted all the imports so that it's shorter
export class HomeScreen extends Component {
// ... I've omitted navigationOptions and propTypes
render() {
const {
checkins,
customChoiceItems,
menuItemPrices,
menuItems,
orders,
pickedRestaurant,
tables,
waitercalls
} = this.props;
console.log("Rendering HomeScreen");
return (
<SafeAreaView style={styles.container}>
<View style={styles.activeOrders}>
<OrdersList
checkins={checkins}
customChoiceItems={customChoiceItems}
menuItemPrices={menuItemPrices}
menuItems={menuItems}
orders={orders}
restaurantSlug={pickedRestaurant.slug}
tables={tables}
waitercalls={waitercalls}
/>
</View>
<View style={styles.tableOvewView}>
<TableOverview
checkins={checkins}
orders={orders}
tables={tables}
waitercalls={waitercalls}
/>
</View>
</SafeAreaView>
);
}
}
const mapStateToProps = state => ({
checkins: getAllCheckinsSelector(state),
customChoiceItems: getAllCustomChoiceItemsNormalizedSelector(state),
menuItemPrices: getAllMenuItemPricesNormalizedSelector(state),
menuItems: getAllMenuItemsNormalizedSelector(state),
orders: getActiveOrdersSelector(state),
pickedRestaurant: getPickedRestaurantSelector(state),
tables: getAllTablesSelector(state),
waitercalls: getAllActiveWaitercallsSelector(state)
});
export default connect(mapStateToProps)(HomeScreen);
OrdersList (as you can see OrdersList also allows presses for orders, which displays the same erroneous behaviour of not having the TableOverView rerender), which is the left list with the clickable <ListItem />s.
import React, { PureComponent } from "react";
import { FlatList, Image, Text } from "react-native";
import { ListItem } from "react-native-elements";
import { connect } from "react-redux";
import PropTypes from "prop-types";
// ... omitted imports
export class OrdersList extends PureComponent {
// omitted propTypes
keyExtractor = item => item.uuid;
registerItem = item => {
// Remember the order status, in case the request fails.
const { restaurantSlug, setOrders } = this.props;
const itemStatus = item.orderStatus;
const data = {
restaurant_slug: restaurantSlug,
order_status: "registered",
order_uuid: item.uuid
};
setOrders({
entities: { orders: { [item.uuid]: { ...item, orderStatus: data.order_status } } }
});
postOrderStatusCreate(data)
.then(() => {})
.catch(err => {
// If the request fails, revert the order status change and display an alert!
alert(err);
setOrders({ entities: { orders: { [item.uuid]: { ...item, orderStatus: itemStatus } } } });
});
};
answerWaitercall = item => {
const { restaurantSlug, setWaitercalls } = this.props;
const data = {
done: true,
restaurant_slug: restaurantSlug
};
setWaitercalls({ entities: { waitercalls: { [item.uuid]: { ...item, done: true } } } });
putUpdateWaitercall(item.uuid, data)
.then(() => {})
.catch(err => {
alert(err);
setWaitercalls({ entities: { waitercalls: { [item.uuid]: { ...item, done: false } } } });
});
};
renderItem = ({ item }) => {
const { checkins, customChoiceItems, menuItemPrices, menuItems, tables } = this.props;
return item.menuItem ? (
<ListItem
title={`${item.amount}x ${menuItems[item.menuItem].name}`}
leftElement={
<Text style={styles.amount}>
{tables.find(table => table.checkins.includes(item.checkin)).tableNumber}
</Text>
}
rightTitle={`${
menuItemPrices[item.menuItemPrice].label
? menuItemPrices[item.menuItemPrice].label
: menuItemPrices[item.menuItemPrice].size
? menuItemPrices[item.menuItemPrice].size.size +
menuItemPrices[item.menuItemPrice].size.unit
: ""
}`}
subtitle={`${
item.customChoiceItems.length > 0
? item.customChoiceItems.reduce((acc, customChoiceItem, index, arr) => {
acc += customChoiceItems[customChoiceItem].name;
acc += index < arr.length - 1 || item.wish ? "\n" : "";
return acc;
}, "")
: null
}${item.wish ? "\n" + item.wish : ""}`}
onPress={() => this.registerItem(item)}
containerStyle={styles.alignTop}
bottomDivider={true}
/>
) : (
<ListItem
title={
item.waitercallType === "bill"
? SCREEN_TEXT_HOME_BILL_CALLED
: SCREEN_TEXT_HOME_SERVICE_ASKED
}
leftElement={
<Text style={styles.amount}>
{
tables.find(table =>
table.checkins.includes(
checkins.find(checkin => checkin.consumer === item.consumer).uuid
)
).tableNumber
}
</Text>
}
rightIcon={{
type: "ionicon",
name: item.waitercallType === "bill" ? "logo-euro" : "ios-help-circle-outline"
}}
onPress={() => this.answerWaitercall(item)}
bottomDivider={true}
/>
);
};
render() {
const { orders, waitercalls } = this.props;
return (
<FlatList
keyExtractor={this.keyExtractor}
data={[...orders, ...waitercalls]}
renderItem={this.renderItem}
// ... omitted ListHeader and ListEmpty properties
/>
);
}
}
export default connect(
null,
{ setOrders, setWaitercalls }
)(OrdersList);
TableOverview, which is the right <FlatList />:
import React, { Component } from "react";
import { FlatList } from "react-native";
import PropTypes from "prop-types";
// ... omitted imports
export class TableOverview extends Component {
// ... omitted propTypes
keyExtractor = item => item.uuid;
renderItem = ({ item }) => {
const { checkins, orders, waitercalls } = this.props;
if (item.invisible) return <Table table={item} />;
console.log("Rendering TableOverview");
return (
<Table
table={item}
hasActiveOrders={orders.some(order => item.userOrders.includes(order.uuid))}
billWanted={item.checkins.some(checkin =>
waitercalls.some(
waitercall =>
waitercall.waitercallType === "bill" &&
waitercall.consumer ===
checkins.find(checkinObj => checkinObj.uuid === checkin).consumer
)
)}
serviceWanted={item.checkins.some(checkin =>
waitercalls.some(
waitercall =>
waitercall.waitercallType === "waiter" &&
waitercall.consumer ===
checkins.find(checkinObj => checkinObj.uuid === checkin).consumer
)
)}
/>
);
};
formatData = (data, numColumns) => {
const numberOfFullRows = Math.floor(data.length / numColumns);
let numberOfElementsLastRow = data.length - numberOfFullRows * numColumns;
while (numberOfElementsLastRow !== numColumns && numberOfElementsLastRow !== 0) {
data.push({ uuid: `blank-${numberOfElementsLastRow}`, invisible: true });
numberOfElementsLastRow++;
}
return data;
};
render() {
const { tables } = this.props;
return (
<FlatList
style={styles.container}
keyExtractor={this.keyExtractor}
data={this.formatData(tables, NUM_COLUMNS)}
renderItem={this.renderItem}
numColumns={NUM_COLUMNS}
/>
);
}
}
export default TableOverview;
I found the solution!
The List was not rerendering, because the <FlatList /> only looked at the tables and not the waitercalls.
I had to add the following property to the <FlatList />:
extraData={[...checkins, ...orders, ...waitercalls]}

Resources