How to use ReactNative FlatList ScrolltoIndex properly? - javascript

I'm a newbie in react native and I'm running into an issue I didn't manage to solve by myself.
So far this is how I am using it:
export default class Form extends React.Component {
state = {index: 0};
_keyExtractor = (item, index) => item.id;
myscrollToIndex = () => {
this.setState({index: ++this.state.index});
this.flatListRef.scrollToIndex({animated: true,index: this.state.index});
};
_renderItem = ({item}) => (
<View>
<Question {...item} />
<Button label='Next' onPress={this.myscrollToIndex} style={styles.buttonNext}/>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
horizontal={false}
ref={(ref) => { this.flatListRef = ref; }}
scrollEnabled
data={this.props.form}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem} />
</View>
);
}
}
I would like to pass the index to myscrolltoindex function but I don't manage to as this.flatListRef gets ruled out when I do that.
Have anyone run into a similar issue ?
Thank you for your time

Use index param as well. And pass the index around.
For e.g _renderItem = ({item, index}) => ( <View> <Question {...item} /> <Button label='Next' onPress={this.myscrollToIndex(index) } style={styles.buttonNext}/> </View> )

Related

React native: Update value of object in array in state

I have a component which changes the state when checkbox is checked and the data needs to be updated of the object in the array.
The component state looks something like this
{
key:1,
todo:"Something",
isChecked:false
}
i have 3 files:
AddTodo.js Which passes state & setState to an component TodoList which passes it the subcomponent TodoItem.
I am unable to update the state from TodoItem , I need to implement a function that finds the object from array and updates its isChecked state.
AddTodo.js
function AddTodo() {
const [state, setState] = useState(false);
const [todos, addTodos] = useState([]);
var keys = (todos || []).length;
return (
<View style={styles.container}>
<Modal
animationType="slide"
transparent={true}
visible={state}
statusBarTranslucent={true}
>
<View style={styles.itemsContainer}>
<GetInfoDialog
state={state}
stateChange={setState}
addItem={addTodos}
numKeys={keys}
/>
</View>
</Modal>
{(todos || []).length > 0 ? (
<TodoList data={todos} updateState={addTodos} />
) : null}
<TouchableOpacity
style={styles.btn}
onPress={() => {
setState(true);
}}
>
<Text style={styles.text}>Add New</Text>
</TouchableOpacity>
</View>
);
}
TodoList.js
function TodoList(props) {
return (
<View style={styles.todoList}>
<FlatList
data={props.data}
renderItem={({ item }) => {
console.log(item);
return (
<TodoItem
list={props.data}
itemKey={item.key}
todo={item.todo}
isChecked={item.isChecked}
updateState={props.updateState}
/>
);
}}
backgroundColor={"#000000"}
alignItems={"center"}
justifyContent={"space-between"}
/>
</View>
);
}
TodoItem.js
function TodoItem(props) {
const [checked, setCheck] = useState(props.isChecked);
return (
<View style={styles.todoItem}>
<Checkbox
value={checked}
onValueChange={() => {
setCheck(!checked);
}}
style={styles.checkbox}
/>
<Text style={styles.text}>{props.todo}</Text>
</View>
);
}
renderItem={({ item, index }) => {
console.log(item);
return (
<TodoItem
list={props.data}
itemKey={item.key}
todo={item.todo}
isChecked={item.isChecked}
updateState={props.updateState}
setChecked={(value)=>{
let updatedList = [...yourTodosList]
updatedlist[index].isChecked=value
setTodos(updatedList)
}}
/>
);
}}
and in your todo item
onValueChange={(value) => {
props.setChecked(value);
}}
i also don't think that you need an is checked state in your todo component since you are passing that through props (so delete const [checked, setCheck] = useState(props.isChecked) line and just use the value you are getting from props.isChecked)
didn't pay much attention to your variable names but this should put you on the right track
as per React Native Hooks you have to call
useEffect(() => {
setCheck(checked);
}, [checked]) // now this listens to changes in contact
in TodoItem.tsx

React Native Carousel UseState

I am currently implementing a carousel library (react-native-reanimated-carousel). The code for the following is represented as such:
<Carousel
width={cardWidth}
height={cardHeight}
data={cards}
onScrollEnd={() => {console.log('ended')}}
renderItem={({item}) =>
(
<View>
<Image
source={{uri: item["ImgURL"],}}
style={styles.card}
/>
</View>
)}
/>
Upon the carousel changing, the item value in the renderItem property changes. Is there a way to get the value of item from outside this element? I want other elements outside to change depending on the value (and properties) of item.
You could try to call a callback function in the renderItem function.
() => {
const [activeItem, setActiveItem] = useState(cards[0].item);
return (<Carousel
width={cardWidth}
height={cardHeight}
data={cards}
onScrollEnd={() => {console.log('ended')}}
renderItem={({item}) => {
setActiveItem(item);
return (
<View>
<Image
source={{uri: item["ImgURL"],}}
style={styles.card}
/>
</View>
);
}}
/>)
);
};
Or you could use onScrollEnd. It provides the previous index and the current index according to the documentation
() => {
const [activeItem, setActiveItem] = useState(cards[0].item);
return (<Carousel
width={cardWidth}
height={cardHeight}
data={cards}
onScrollEnd={(previous, current) => {
setActiveItem(cards[current].item);
console.log('ended')
}}
renderItem={({item}) => (
<View>
<Image
source={{uri: item["ImgURL"],}}
style={styles.card}
/>
</View>
)}
/>)
);
};
Or you could use the Ref prop getCurrentIndex

how to have flat list render only once?

I am trying to print the ComponentTwo Flatlist only once but instead, I am getting the result image1 but instead, I need it to appear like this image 2. I have attached a snack link with the code in it.
Code That will produce the same results as in the images
Expo Snack Link
Working Example: Expo Snack
Here is how you can fix this, first pass the index value to ComponentOne from App.js
const App = () => {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={({item, index}) => <ComponentOne name={item.title} index={index}/>}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
};
Now use that prop value to render ComponentTwo conditionally in ComponentOne like shown below:
//...const
ComponentOne = (props: ComponentOneProps) => {
return (
<View style={{ margin: 15, backgroundColor: 'yellow' }}>
<FlatList
data={recent}
renderItem={({ item }) => {
console.log("hello")
// #ts-ignore
return props.index == 0?<ComponentTwo name={item.name} />:null;
}}
keyExtractor={(item) => item.idd}
/>
//...

Changing style of specific component returned from map function onClick

I am trying to change the style of individual TouchableOpacity components that have been returned from a map function.
Here is the component:
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<React.Fragment key={id}>
<TouchableOpacity
style={styles.button}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
Let TouchableOpacity = TO.
The map function returns about 30 TOs with unique IDs. When I click the TOs, I can see their unique ID in the console log. I want to know how I can modify the style of an individual TO.
Here is my render function which uses the functional component Example.
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList() }} />
</View>
</ScrollView>
</View>
);
}
What I have tried:
referencing this stackoverflow post, I tried to create a function which changed the style of the TO when it is clicked. But the result of this changed all the TOs in the UI since of the way it is mapped.
I tried something like the following.
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
let buttonStyle = this.state.pressed ? styles.button : styles.buttonClicked
return (
<React.Fragment key={id}>
<TouchableOpacity
style={buttonStyle}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
But as previously stated, this changed all of the Touchable Opacitys. Is there a way to only change one?
Thanks
Edit - to show entire class
class Page extends Component {
constructor(props) {
super(props)
}
MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
);
}
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id}/>
);
})}
</View>
);
};
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList()}} />
</View>
</ScrollView>
</View>
);
}
}
It is easier to separate the component inside map to a separate component and then handle style changes on press there
const MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
)
}
so you can use in the map like this
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id} />
);
})}
</View>
);
};

How to pass a function/method as a parameter in react navigation.navigate

I have two components named Home.js and ViewList.js the user can navigate to each component using a stack navigator.
I want to pass the updatePrice function as a parameter from Home.js to ViewList.js below are snippets on what I am attempting to accomplish.
Here is a brief description of how the app is suppose to work. The user navigates from the Home.js component
to the ViewList.js component. In the View.js component the user has the ability to enter a price for the item.
onChangeText I want to run the updatePrice function so that I can update the state of the list item in the Home.js component.
My problem is that I cannot seem to figure out how to pass the updatePrice function to the ViewList through. Any suggestions
on how I can accomplish this ?
Below is a code snippet for Home.js
export default function Home({navigation}){
const updatePrice = (key, id, price) =>{
setShoppingList(list =>
list.map(list =>
list.key === key
? {
...list,
listItems: list.listItems.map(item =>
item.id === id
? {
...item,
price
}
:item
)
}
: list
)
)
console.log(shoppingLists);
}
return(
<View style={globalStyles.container}>
<FlatList
data={shoppingLists}
renderItem={({ item }) =>(
<TouchableOpacity onPress={() =>
// Here I try to pass the function through the
//navigate prop as an object.
navigation.navigate('ViewList',item,{'updatePrice': () => this.updatePrice})}>
{/*Create card container to wrap text in*/}
<Card>
<Text>{item.name}</Text>
<Text>Budget: $ {item.budget}</Text>
</Card>
</TouchableOpacity>
)}
keyExtractor={item => item.key}
/>
</View>
)
}
Here is another ViewList.js
export default function ViewList({navigation}){
const shoppingListKey = navigation.getParam('key');
const updatePrice = navigation.getParam('updatePrice');
const listInfo = navigation.getParam('listItems');
return(
<View style={globalStyles.container}>
<Card>
<Text>{navigation.getParam('name')}</Text>
<Text>Budget: $ {budget}</Text>
<Text> Total: {total}</Text>
<FlatList
data={listInfo}
renderItem={({item}) =>(
<View>
<Text>{item.name}</Text>
<Text>Price: {item.price}</Text>
<TextInput
style={globalStyles.globalTextInput}
keyboardType={'numeric'}
onChangeText={(value) =>
//Here I try to retrieve the updatePrice function but I got an error.
this.props.navigation.state.params.updatePrice(shoppingListKey,item.id,value)}
/>
</View>
)}
keyExtractor={item => item.id}
/>
</Card>
</View>
)
}

Categories

Resources