Change ad banner for react native application - react-native

I have an advertising banner at the bottom of the main page of the application! I want to change it on a timer to another banner!
Some Screen:
function SomeScreen() {
return (
<View>
...
<MyBanner></MyBanner>
</View>
)
}
Banner Component:
const adUnitIds = ['ca-app-pub-eeeeeeee/rrrrrrrr', 'ca-app-pub-ffffff/ggggggggg', 'ca-app-pub-yyyyyyyyy/xxxxxxxxx']
let activeNum = 0
function MyBanner({}) {
const [activeAdUnitId, setActiveAdUnitId] = useState(adUnitIds[0])
useEffect( () => {
const interval = setInterval( () => {
activeNum = activeNum === adUnitIds.length - 1 ? 0 : activeNum + 1
setActiveAdUnitId(adUnitIds[activeNum])
}, 20000)
return () => {
clearInterval(interval)
}
}, [])
return (
<View>
<BannerAd
unitId={activeAdUnitId}
size={BannerAdSize.ANCHORED_ADAPTIVE_BANNER }
requestOptions={{
requestNonPersonalizedAdsOnly: true
}}
/>
</View>
)
}
It works, but would like to know about best practices! How good is this solution?

Related

FlatList in ReactNative does not update/re-render when its data changes

Hej, I advanced my FlatList in React Native with
a) inbox/archive views
and b) with standard filter functionalities.
It's working somehow, but is not production ready!
Can someone please check this (I think well-organized) code and tell me where I do what wrong?
What is not working:
a) FlatList does not always re-render/update when the stream state, which is its data prop, changes
b) FlatList does not remove an item immediately when I archive/unarchive via swipe functionality. I have to manually change the view to see ...
c) FlatList does not directly apply the filter on state, I have to click twice to make it happen ...
//React
import { View, StyleSheet, Pressable, Animated, FlatList } from "react-native";
import { useCallback, useContext, useEffect, useState, useMemo } from "react";
//Internal
import SelectBtn from "./SelectBtn";
import SessionComponent from "./SessionComponent";
import LoadingOverlay from "../notification/LoadingOverlay";
import { SessionsContext } from "../../store/context-reducer/sessionsContext";
//External
import { Ionicons } from "#expo/vector-icons";
import { useNavigation } from "#react-navigation/native";
import { database, auth } from "../../firebase";
import { ref, onValue, remove, update } from "firebase/database";
function SessionStream() {
const navigation = useNavigation();
const sessionsCtx = useContext(SessionsContext);
const currentSessions = sessionsCtx.sessions;
const [isFetching, setIsFetching] = useState(true);
const [stream, setStream] = useState([]);
const [inbox, setInbox] = useState([]);
const [archive, setArchive] = useState([]);
const [filter, setFilter] = useState([]);
const sessionList = ["Sessions", "Archive"];
const sortList = ["ABC", "CBA", "Latest Date", "Earliest Date"];
useEffect(() => {
//Fetches all sessions from the database
async function getSessions() {
setIsFetching(true);
const uid = auth.currentUser.uid;
const sessionsRef = ref(database, "users/" + uid + "/sessions/");
try {
onValue(sessionsRef, async (snapshot) => {
const response = await snapshot.val();
if (response !== null) {
const responseObj = Object.entries(response);
const sessionsData = responseObj.map((item) => {
return {
id: item[1].id,
title: item[1].title,
laps: item[1].laps,
endTime: item[1].endTime,
note: item[1].note,
identifier: item[1].identifier,
date: item[1].date,
smed: item[1].smed,
externalRatio: item[1].externalRatio,
internalRatio: item[1].internalRatio,
untrackedRatio: item[1].untrackedRatio,
archived: item[1].archived,
};
});
sessionsCtx.setSession(sessionsData);
setIsFetching(false);
} else {
sessionsCtx.setSession([]);
setIsFetching(false);
}
});
} catch (err) {
alert(err.message);
setIsFetching(false);
}
}
getSessions();
}, []);
useEffect(() => {
//Sorts sessions into archived and unarchived
setInbox(
currentSessions.filter((session) => {
return session.archived === false || session.archived === undefined;
})
);
setArchive(
currentSessions.filter((session) => {
return session.archived === true;
})
);
}, [currentSessions, archiveHandler, unArchiveHandler, sessionsCtx, stream]);
if (isFetching) {
setTimeout(() => {
return <LoadingOverlay />;
}, 5000);
}
const onPressHandler = useCallback(
//Callback to open the session
(item) => {
navigation.navigate("Detail", {
sessionID: item.id,
});
},
[onPressHandler]
);
const rightSwipeActions = useCallback(
//Swipe actions for the session list
(item, swipeAnimatedValue) => {
return (
<View
style={{
flexDirection: "row",
width: 168,
height: 132,
}}
>
{item.archived === false ? (
<Pressable
onPress={archiveHandler.bind(this, item)}
style={({ pressed }) => pressed && styles.swipePressed}
>
<View style={styles.archive}>
<Animated.View
style={[
styles.archive,
{
transform: [
{
scale: swipeAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: "clamp",
}),
},
],
},
]}
>
<Ionicons
name="ios-archive-outline"
size={24}
color="white"
/>
</Animated.View>
</View>
</Pressable>
) : (
<Pressable
onPress={unArchiveHandler.bind(this, item)}
style={({ pressed }) => pressed && styles.swipePressed}
>
<View style={styles.unArchive}>
<Animated.View
style={[
styles.unArchive,
{
transform: [
{
scale: swipeAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: "clamp",
}),
},
],
},
]}
>
<Ionicons
name="md-duplicate-outline"
size={24}
color="white"
/>
</Animated.View>
</View>
</Pressable>
)}
<Pressable
onPress={deleteHandler.bind(this, item)}
style={({ pressed }) => pressed && styles.pressed}
>
<View style={styles.trash}>
<Animated.View
style={[
styles.trash,
{
transform: [
{
scale: swipeAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: "clamp",
}),
},
],
},
]}
>
<Ionicons name="trash-outline" size={24} color="white" />
</Animated.View>
</View>
</Pressable>
</View>
);
},
[rightSwipeActions]
);
const deleteHandler = useCallback(
(item) => {
try {
sessionsCtx.deleteSession(item.id); // delete from local context
const uid = auth.currentUser.uid;
const sessionRef = ref(
database,
"users/" + uid + "/sessions/" + item.id
);
remove(sessionRef); // delete from firebase
} catch (error) {
alert(error.message);
}
},
[deleteHandler]
);
const archiveHandler = (item) => {
try {
const id = item.id;
const updatedSession = {
...item, // copy current session
archived: true,
};
const uid = auth.currentUser.uid;
const sessionRef = ref(database, "users/" + uid + "/sessions/" + id);
update(sessionRef, updatedSession);
/* sessionsCtx.updateSession(id, updatedSession); */
//update inbox state
setInbox(
currentSessions.filter((session) => {
const updatedData = session.archived === false;
return updatedData;
})
);
//update archive state
setArchive(
currentSessions.filter((session) => {
const updatedData = session.archived === true;
return updatedData;
})
);
} catch (error) {
alert(error.message);
}
};
const unArchiveHandler = (item) => {
try {
const id = item.id;
const updatedSession = {
...item, // copy current session
archived: false,
};
const uid = auth.currentUser.uid;
const sessionRef = ref(database, "users/" + uid + "/sessions/" + id);
update(sessionRef, updatedSession);
/* sessionsCtx.updateSession(id, updatedSession); */
//update unarchived session list
setArchive((preState) => {
//remove the item from archived list
preState.filter((session) => session.id !== item.id);
return [...preState];
});
} catch (error) {
alert(error.message);
}
};
const selectSessionHandler = useCallback(
(selectedItem) => {
switch (selectedItem) {
case "Sessions":
setStream(inbox);
break;
case "Archive":
setStream(archive);
break;
}
},
[selectSessionHandler, inbox, archive]
);
const selectFilterHandler = (selectedItem) => {
//filter the session list
switch (selectedItem) {
case "ABC":
// Use the Array.sort() method to sort the list alphabetically in ascending order
const sortedList = stream.sort((a, b) => {
return a.title.localeCompare(b.title);
});
setStream((preState) => {
return [...sortedList];
});
break;
case "CBA":
// Use the Array.sort() method to sort the list alphabetically in descending order
const sortedList2 = stream.sort((a, b) => {
return b.title.localeCompare(a.title);
});
setStream((preState) => {
return [...sortedList2];
});
break;
case "Latest Date":
// Use the Array.sort() method to sort the list by date in descending order
const sortedList3 = stream.sort((a, b) => {
return b.date.localeCompare(a.date);
});
setStream((preState) => {
return [...sortedList3];
});
break;
case "Earliest Date":
// Use the Array.sort() method to sort the list by date in ascending order
const sortedList4 = stream.sort((a, b) => {
return a.date.localeCompare(b.date);
});
setStream((preState) => {
return [...sortedList4];
});
break;
}
};
const renderSessionItem = useCallback(({ item }) => {
return (
<Pressable
/* style={({ pressed }) => pressed && styles.pressed} */
onPress={onPressHandler.bind(null, item)}
key={item.id}
>
<SessionComponent
key={item.id}
title={item.title}
identifier={item.identifier}
date={item.date}
rightSwipeActions={rightSwipeActions.bind(null, item)}
smed={item.smed}
endTime={item.endTime}
/>
</Pressable>
);
}, []);
return (
<View style={styles.container}>
<View style={styles.menuRow}>
<SelectBtn
data={sortList}
onSelect={(item) => selectFilterHandler(item)}
/>
<SelectBtn
data={sessionList}
onSelect={(item) => selectSessionHandler(item)}
/>
</View>
<FlatList
data={stream}
renderItem={renderSessionItem}
keyExtractor={(item) => item.id}
extraData={stream}
/>
</View>
);
}
export default SessionStream;
What I already tried:
I tried ChatGPT the whole day yesterday ... ;-)
I tried updating the global state for sessions to trigger re-renders ...
I tried updating the local state as obj or via the spread operator to ...
I tried extraData prop at FlatList
I removed useCallback to make sure it doesnt somehow block ...
can you do this when you are updating your stream state
const oldStream = stream;
const newStream = newStream; // put the value that you have updated
const returnedTarget= Object.assign(stream, newStream);
setStream(returnedTarget);
The problem might be you are mutating the copy obj
ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
c) in your selectFilterHandler method you are updating filter and in the same method you are trying to use updated filter value. which is not possible as useState does not update value immediately.
b) in your archiveHandler i think you are not updating the setStream state. if you are trying to run
const selectSessionHandler = useCallback(
(selectedItem) => {
switch (selectedItem) {
case "Sessions":
setStream(inbox);
break;
case "Archive":
setStream(archive);
break;
}
},
[selectSessionHandler, inbox, archive]
);
this method whenever inbox/archive changes it will not work it will be work only when you call this method and any of the value in dependency array has changed. (you can use useEffect and pass and deps arr [inbox,archive] which will run every time and can update your state.

if action.payload.number exist increase state.points with action.payload.points

In React-Native I´ve two TextInputs and an add-button
My add function works i.e it creates a list. But each time I fill out the form, it adds a new listpost. I want to check if action.payload.number exist, and if true, increase state.points with action.payload.points.
as yet everything I tried with have failed i.e it didn't recognize state.number === action.payload.number and adds a new row in the list
Please help me solving this issue
Thanks in advance
Pierre
const InputScreen = () => {
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const {addScorer} = useContext(Context);
const onPress = () => {
addScorer(number, points);
setnumber("");
setPoints("");
};
return (
<View>
<Text style={styles.label}>enter Scorer Number:</Text>
<TextInput style={styles.input} value={number} onChangeText={setNumber} />
<Text style={styles.label}>enter Points Scored:</Text>
<TextInput style={styles.input} value={points} onChangeText={setPoints} />
<TouchableOpacity onPress={onPress}>
<FontAwesome5 name="plus-circle" size={44} color="coral" />
</TouchableOpacity>
</View>
);
};
export default InputScreen;
const scorerReducer = (state, action) => {
const id = Date.now().toString().slice(-4);
switch (action.type) {
case "add_scorer":
// if (state.number === action.payload.nummer) {
if (state.find((scorer) => scorer.id === action.payload.id)) {
return [
...state,
{
points: state.points + +action.payload.points,
},
];
} else {
return [
...state,
{
id: id,
number: action.payload.number,
points: +action.payload.points,
},
];
}
default:
return state;
}
};
const addScorer = (dispatch) => {
return (number, points, id) => {
dispatch({type: "add_scorer", payload: {number, points, id}});
};
};
export const {Context, Provider} = createDataContext(
scorerReducer,
{addScorer},
[]
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I solved it without using Reducer:)
export const ScoredProvider = ({children}) => {
const [playerList, setPlayerList] = useState([]);
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const addScorer = () => {
const players = [...playerList];
if (number.trim().length === 0) {
return;
}
const posit = players.map((player) => player.number).indexOf(number);
if (posit !== -1) {
setPlayerList((playerList) =>
playerList.map((scorer, index) =>
index === posit
? {
...scorer,
points: scorer.points + +points,
}
: scorer
)
);
} else {
const newScorer = {
id: Date.now(),
number: number,
points: +points,
};
setPlayerList([...playerList, newScorer]);
setPoints(points);
}
};
return (
<ScoredContext.Provider
value={{number, setNumber, points, setPoints, playerList, addScorer}}
>
{children}
</ScoredContext.Provider>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Countdown timer in react-native

I want to countdown from 3 to 1 when a screen is loaded in react-native. I tried it with setTimeOut like this and it didn't work. What am I doing wrong here? How can I achieve this? When the screen is loaded, I want to show 3 =-> 2 ==> 1 with 1 second interval. Here is my code.
constructor(props) {
super(props);
this.state = {
timer: 3
}
}
// componentDidMount
componentDidMount() {
setTimeout(() => {
this.setState({
timer: --this.state.timer
})
}, 1000);
}
In your code setTimeout is called in componentDidMount and ComponetDidMount will be called once in whole component lifeCycle. So, the function within setTimeout will be called once only. i.e. just after the first render but upon successive render, the componentDidMount won't be called.
Solution to your problem can be:
1. Class Component
constructor(props: Object) {
super(props);
this.state ={ timer: 3}
}
componentDidMount(){
this.interval = setInterval(
() => this.setState((prevState)=> ({ timer: prevState.timer - 1 })),
1000
);
}
componentDidUpdate(){
if(this.state.timer === 1){
clearInterval(this.interval);
}
}
componentWillUnmount(){
clearInterval(this.interval);
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', }}>
<Text> {this.state.timer} </Text>
</View>
)
}
'setInterval' vs 'setTimeout'
Advantage of using a function in setState instead of an object
memory leak because of setInterval:
if we unmount the component before clearInterval called, there is a memory leak because the interval that is set when we start and the timer is not stopped. React provides the componentWillUnmount lifecycle method as an opportunity to clear anything that needs to be cleared when the component is unmounted or removed.
2. Functional Component
function CountDownTimer(props) {
const [time, setTime] = React.useState(props.initialValue || 10);
const timerRef = React.useRef(time);
React.useEffect(() => {
const timerId = setInterval(() => {
timerRef.current -= 1;
if (timerRef.current < 0) {
clearInterval(timerId);
} else {
setTime(timerRef.current);
}
}, 1000);
return () => {
clearInterval(timerId);
};
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<Text> {time} </Text>
</View>
)
}
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component. So on component rerender the object reference will be same.
Answers given by #TheEhsanSarshar and #Rishabh Jain will also work. I have shown a slightly different solution from the others.
Updated Hooks (using useEffect) version to countdown using setInterval in react-native:
const [timerCount, setTimer] = useState(60)
useEffect(() => {
let interval = setInterval(() => {
setTimer(lastTimerCount => {
lastTimerCount <= 1 && clearInterval(interval)
return lastTimerCount - 1
})
}, 1000) //each count lasts for a second
//cleanup the interval on complete
return () => clearInterval(interval)
}, []);
use the state variable timerCount as: <Text>{timerCount}</Text>
Usage:
timestamp prop must be in seconds
const refTimer = useRef();
const timerCallbackFunc = timerFlag => {
// Setting timer flag to finished
console.warn(
'You can alert the user by letting him know that Timer is out.',
);
};
<Timer
ref={refTimer}
timestamp={moment(item?.time_left).diff(moment(), 'seconds')}
timerCallback={timerCallbackFunc}
textStyle={styles.timerTextAHL}
/>
Timer.js
import React, {
useState,
useEffect,
useRef,
forwardRef,
useImperativeHandle,
} from 'react';
import { Text, View } from 'react-native';
const Timer = forwardRef((props, ref) => {
// For Total seconds
const [timeStamp, setTimeStamp] = useState(
props.timestamp ? props.timestamp : 0,
);
// Delay Required
const [delay, setDelay] = useState(props.delay ? props.delay : 1000);
// Flag for informing parent component when timer is over
const [sendOnce, setSendOnce] = useState(true);
// Flag for final display time format
const [finalDisplayTime, setFinalDisplayTime] = useState('');
useInterval(() => {
if (timeStamp > 0) {
setTimeStamp(timeStamp - 1);
} else if (sendOnce) {
if (props.timerCallback) {
props.timerCallback(true);
} else {
console.log('Please pass a callback function...');
}
setSendOnce(false);
}
setFinalDisplayTime(secondsToDhms(timeStamp));
}, delay);
function secondsToDhms(seconds) {
seconds = Number(seconds);
var d = Math.floor(seconds / (3600 * 24));
var h = Math.floor((seconds % (3600 * 24)) / 3600);
var m = Math.floor((seconds % 3600) / 60);
var s = Math.floor(seconds % 60);
var dDisplay = d > 0 ? d + 'd ' : '';
var hDisplay = h > 0 ? h + 'h ' : '';
var mDisplay = m > 0 ? m + 'm ' : '';
var sDisplay = s > 0 ? s + 's ' : '';
return dDisplay + hDisplay + mDisplay + sDisplay;
}
const refTimer = useRef();
useImperativeHandle(ref, () => ({
resetTimer: () => {
// Clearing days, hours, minutes and seconds
// Clearing Timestamp
setTimeStamp(props.timestamp);
setSendOnce(true);
},
}));
return (
<View ref={refTimer} style={props.containerStyle}>
<Text style={props.textStyle}>{sendOnce ? finalDisplayTime : '0'}</Text>
</View>
);
});
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest function.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => {
clearInterval(id);
};
}
}, [delay]);
}
export default Timer;
The hooks version.
function CountDown() {
const [count, setCount] = useState(3)
useEffect(() =>
let interval = setInterVal(() => {
setCount(prev => {
if(prev === 1) clearInterval(interval)
return prev - 1
})
})
// interval cleanup on component unmount
return () => clearInterval(interval)
), [])
return <Text>{count}</Text>
}
If anyone wants to start the timer again on a button press, this will be the code in react-hooks:
let timer = () => {};
const myTimer = () => {
const [timeLeft, setTimeLeft] = useState(30);
const startTimer = () => {
timer = setTimeout(() => {
if(timeLeft <= 0){
clearTimeout(timer);
return false;
}
setTimeLeft(timeLeft-1);
}, 1000)
}
useEffect(() => {
startTimer();
return () => clearTimeout(timer);
});
const start = () => {
setTimeLeft(30);
clearTimeout(timer);
startTimer();
}
return (
<View style={styles.container}>
<Text style={styles.timer}>{timeLeft}</Text>
<Button onPress={start} title="Press"/>
</View>
)}
Here in this example, I have taken a timer of 30 seconds
Code Of Power
Hope so this Way is Easy
componentDidMount() {
this.CounterInterval()
}
CounterInterval = () => {
this.interval = setInterval(
() => this.setState({
timer: this.state.timer - 1
}, () => {
if (this.state.timer === 0) {
clearInterval(this.interval);
}
}),
1000
);
}

React Native FlatList extraData not rerendering

I am not able to re-render my FlatList component. I have tried everything that I can think of. I have tried updating a boolean value for extraData with this.setState, I have passed Immutable.Map to extraData. But nothing seems to work although I know my component has updated correctly and everything is in place when component re-render, but FlatList does not re-render although I have passed changed data to extraData. I just cant figure out what I am doing wrong.
Here is my component without imports and styles:
class DetailsScreen extends Component {
constructor(props) {
super(props);
const {
data: { list, letterIndexes, alphabet },
savedList
} = this.props.navigation.state.params;
this.state = {
alphabet,
letter: "A",
letterIndexes,
letterIndexesValues: Object.values(letterIndexes),
savedList: savedList || [],
list,
updated: false
};
}
componentWillMount() {
const { list, savedList, refreshFlatList, updated } = this.state;
this.setState({
list: this.updateListActiveState(list, savedList),
updated: !updated
});
}
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.title}`
});
/**
* Adds active prop to each object in array.
* If they exists in savedList then we set active prop as true
*
* #param {Array<Object>} list - Names list
* #param {Array<Object>} savedList - Saved names list
* #returns {Array<Object>} list
*/
updateListActiveState(list, savedList) {
return list.map(item => {
item.active = savedList.some(s => item.name === s.name);
return item;
});
}
/**
* Updates list names state with new state
*/
onClick = savedList => {
const { list, updated } = this.state;
this.setState({
list: this.updateListActiveState(list, savedList),
updated: !updated
});
};
/**
* Renders FlatList single item
*/
renderItem = ({ item }) => {
return <NameItem key={item.id} item={item} onClick={this.onClick} />;
};
onLetterPress(letter) {
const { letterIndexes } = this.state;
this.setState({ letter });
try {
this.flatListRef.scrollToIndex({
animated: true,
index: letterIndexes[letter]
});
} catch (e) {}
}
getItemLayout = (data, index) => {
return { length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index };
};
handleScroll = event => {
const { letterIndexes, letterIndexesValues } = this.state;
const y = event.nativeEvent.contentOffset.y + ROW_HEIGHT;
const index = parseInt(y / ROW_HEIGHT);
let letter = "A";
let cnt = 0;
for (const key in letterIndexes) {
if (letterIndexes.hasOwnProperty(key)) {
const startIndex = letterIndexes[key];
const endIndex =
letterIndexesValues[cnt + 1] !== undefined
? letterIndexesValues[cnt + 1]
: startIndex;
if (startIndex <= index && index <= endIndex) {
letter = key;
break;
}
}
cnt += 1;
}
if (letter !== this.state.letter) {
this.setState({ letter });
}
};
render() {
const { list, letterIndexes, alphabet, savedList, updated } = this.state;
const {
alphabetContainer,
container,
letter,
letterActive,
letterLast
} = styles;
console.log(updated);
return (
<View className={container}>
<FlatList
data={list}
keyExtractor={item => item.id}
renderItem={this.renderItem}
getItemLayout={this.getItemLayout}
initialNumToRender={20}
onScrollEndDrag={this.handleScroll}
extraData={updated}
ref={ref => {
this.flatListRef = ref;
}}
/>
<ScrollView
style={alphabetContainer}
showsHorizontalScrollIndicator={false}
>
<View>
{alphabet.map((l, i) => {
const active = l === this.state.letter ? letterActive : "";
const last = i === alphabet.length - 1 ? letterLast : "";
return (
<Text
key={i}
style={[letter, active, last]}
onPress={() => this.onLetterPress(l)}
>
{l}
</Text>
);
})}
</View>
</ScrollView>
</View>
);
}
}

Update position of All places in react-native-sortable-listview

I am using react-native-sortable-listview in react-native for sorting same places.
constructor() {
this.state = {
makers: [
{ kolkata: 'Hawrah Birdge' },
{ Delhi: 'Lal Kila' },
{ Agra: 'Taj Mahal' },
{ Mumbai: 'India Gate' },
],
allObj: {},
order: []
};
}
componentDidMount() {
const newAllObj = this.getAllObjFromMaker(this.state.makers);
const newOrder = this.getOrderFromMaker(newAllObj);
this.setState({ allObj: newAllObj, order: newOrder });
}
getAllObjFromMaker(makers) {
const allObj = makers.reduce((result, d) => {
result[`${d.coordinate.latitude}_${d.coordinate.longitude}`] = d;
return result;
}, {});
return allObj;
}
getOrderFromMaker(allObj) {
const order = Object.keys(allObj);
return order;
}
renderOneDraggableMilestone(milestone) {
const i = this.state.makers.indexOf(milestone);
return (
<TouchableOpacity {...this.props.sortHandlers}>
<Text>{i + 1}</Text>
<Text>{milestone.address}</Text>
</TouchableOpacity>
);
}
arrangedMilestoneList(e) {
const arr = this.state.makers;
arr.splice(e.to, 0, arr.splice(e.from, 1)[0]);
const newAllObj = this.getAllObjFromMaker(arr);
const newOrder = this.getOrderFromMaker(newAllObj);
this.setState({ makers: arr, allObj: newAllObj, order: newOrder
});
}
render() {
return (
<SortableListView
data={this.state.allObj}
order={this.state.order}
activeOpacity={0.5}
onRowMoved={e => {
this.arrangedMilestoneList(e);
this.forceUpdate();
}}
renderRow={(row) => this.renderOneDraggableMilestone(row)}
/>
);
}
I want to arrange places and also their position in this.state.makers as I am doing using i in renderOneDraggableMilestone. On renderRow only draggable place are render so only their position is updated. And renderRow is last to excute so forceUpdate is also not working.
How to rerender after executing renderRow. So all position could be updated.
Ok I have find a way to re-render as follow.
<SortableListView
key={this.state.count}
data={this.state.allObj}
order={this.state.order}
activeOpacity={0.5}
onRowMoved={e => {
this.setState({ count: this.state.count + 1 });
this.props.arrangedMilestoneList(e);
console.log('onRowMoved is called');
}}
onMoveEnd={() => console.log('onMoveEnd is fired')}
renderRow={(row, s1, i) => this.renderOneDraggableMilestone(row, s1, i)}
/>
What I am doing is I added a key attribute to SortableListView and update this key on each onRowMoved action. And because of this it causes re-render.