Flat List gets hidden once the state gets changed in react native - react-native

I am having a FlatList view with checkbox for each item.when i press a checkbox the state gets changed from false to true at that time the list gets hidden.Does anyone has come through this strange behaviour.
<FlatList
data={this.state.branches}
renderItem={({ item }) => {
{ tempCheckValues[item._id] = false; }
return (
<ListItem avatar key={item._id}>
<Left>
<CheckBox
checked={item.checked}
onPress={this.toggleCheckbox.bind(this, item._id)}
/>
</Left>
<Body>
<Text>{item.branch_name}</Text>
<Text note>{item.formatted_address}</Text>
</Body>
</ListItem>
);
}
}
/>
Check Box Toggle:
toggleCheckbox = (id) => {
const changedCheckbox = this.state.branches.find((branch) => branch._id === id);
console.log('changedCheckbox', changedCheckbox);
changedCheckbox.checked = !changedCheckbox.checked;
const checkboxes = Object.assign({}, this.state.checkboxes, changedCheckbox);
this.setState({ branches: checkboxes });
}

You are mutating the state can be the reason for the problem. It would better to clone the branches state and update the cloned branches and finally setState.
const clonedBranches = JSON.parse(JSON.stringify(this.state.branches));
const updatedBranch = clonedBranches.find(({_id}) => _id === id)
updatedBranch=!updatedBranches.checked;
this.setState({branches: clonedBranches})

inside toggleCheckbox
this.setState({ branches: checkboxes });
but you are getting data in FlatList with branches
<FlatList
data={this.state.branches}
When you call toggleCheckbox, branches array is updated.
Got it?

Related

React Native - Appended component in hook not responding as expected

I am working on a project that uses Google autocomplete to set locations. The project allows users to set pickup and destination location, and then they can also enter stop-by places up to additional 3, making it a total of 5.
Here's my sample code:
const placesRef = useRef([]);
const [stopspots, setStopSpots] = useState([]);
const [state, setState] = useState({
defaultPlacesInput: 'flex',
//and others
});
useEffect(() => {
placesRef.current = placesRef.current.slice(0, 5);
}, []);
const placesComponent = (i, placeholder) => {
return (<PlacesFrame key={i}>
...
<GooglePlacesAutocomplete
placeholder={placeholder}
minLength={2}
ref={el => placesRef.current[i] = el}
onPress={(data, details = null) => {
placesRef.current[i]?.setAddressText(data?.structured_formatting?.main_text);
setState({...state, defaultPlacesInput: 'flex'})
}}
enablePoweredByContainer={false}
fetchDetails
styles={{
textInput: [styles.input1,{paddingLeft:30}],
container: [styles.autocompleteContainer,{display:placesRef.current[i]?.isFocused() ? 'flex' : state.defaultPlacesInput}],
listView: styles.listView,
listView: styles.listView,
row: styles.row,
predefinedPlacesDescription: {
color: '#1faadb',
},
}}
query={{
key: GOOGLE_PLACES_API_KEY,
language: profile.language,
components: 'country:' + profile.iso,
}}
textInputProps={{
//value: '',
onChangeText: alterOtherFields
}}
renderRow={(data) => <PlaceRow data={data} />}
/>
...
</PlacesFrame>)
}
const stopByLocation = () => {
var counter = stopspots.length, obj = placesComponent(counter + 2, 'Drop off location');
setStopSpots([...stopspots, {
id: counter,
place: obj
}
])
}
And here is how the autocomplete component is rendered
return(
...
<View>
{placesComponent(0, 'Pick up location')}
{placesComponent(1, 'Drop off location')}
</View>
...
)
The output look like this
Everything works perfect when I call the placesComponent() function directly. But like I mentioned earlier, I want the users to be able to add up to 3 additional stop by locations, and because it is optional, additional fields is added by appending to hook, and then rendered. the code looks like this.
return(
...
<View>
{placesComponent(0, 'Pick up location')}
{placesComponent(1, 'Drop off location')}
//This will append more placed fields
{stopspots != '' ?
stopspots.map((item : {}) => ((item.place)))
: null}
<ClickableButton>
<TouchableOpacity activeOpacity={0.6} onPress={() => stopByLocation()}><AddPlaces><AntDesign name="plus" size={10} color="#444" /> Add</AddPlaces></TouchableOpacity>
</ClickableButton>
</View>
...
)
The outcome looks like this
I observed that each component binded to the hooks takes the early properties, and does not effect additional changes. While the first two fields rendered by calling the function directly does.
When I make changes to state.defaultPlacesInput (observe this in styles property of GooglePlacesAutocomplete), the changes only effect on the two components called directly.
Is there a module, or a systematic way to append the renderer function call, without using useState hooks to append the 3 additional fields?
Is it possible to expose stored properties in useState hooks to respond as the other two which observe the state changes? If yes, how?
Any contribution, suggestion will be accepted

How to Highlight Updated Items onRefresh of FlatList

I set up a FlatList with an onRefresh function to update the state when the user drags down the screen. It works properly, however I was wondering how I can highlight items in the FlatList that have been updated after the refresh.
Say, for example, I want to change the background for a few seconds for any item in the list that was updated, then return to normal.
<FlatList
data={scores}
renderItem={({item}) => (
<View style={styles.scoreContainer}>
<ScoreRow data={item.away} />
<ScoreRow data={item.home} />
</View>
)}
keyExtractor={item => item.gameID}
refreshing={isRefreshing}
onRefresh={updateScores}
/>
The best I could do was add a useEffect in the ScoreRow component to detect if something changes within that component, but that only allows me to update one component at a time, not the entire View.
const [runUpdate, setRunUpdate] = useState(false)
const [runs, setRuns] = useState(data.R)
useEffect(() => {
if(runs !== data.R) {
setRunUpdate(true)
setRuns(data.R)
setTimeout(() => setRunUpdate(false), 10000)
}
}, [data.R])
I can't figure out how to detect a change on an an item in the View of the FlatList so that I can change the entire View the way I did each component.
You can achieve this by using data of FlatList. You have to make an extra parameter for this.
eg:
//Method to refresh data
_refreshMethod() {
// Do your code to fetch...
...
let newDataArray = data // Data fetch from server or some thing.
let updatedArray = []
newDataArray.map((data, index) => {
data["isNewItem"] = true;
updatedArray.push(data);
});
this.setState({scores: updatedArray})
this._callTimer()
}
// Method to update new item status after a delay
_callTimer() {
setTimeout(function() {
let updatedArray = []
this.state.scores.map((data, index) => {
data["isNewItem"] = false;
updatedArray.push(data);
});
this.setState({scores: updatedArray})
}, 3000); // The time you want to do...
}
Then change the style of row based on the state value.
<FlatList
data={this.state.scores}
renderItem={({item}) => (
<View style={item.isNewItem ? styles.yourNewItemStyle : styles.scoreContainer}>
<ScoreRow data={item.away} />
<ScoreRow data={item.home} />
</View>
)}
keyExtractor={item => item.gameID}
refreshing={isRefreshing}
onRefresh={updateScores}
extraData={this.state}
/>

How to check checkbox when there is an array in React Native?

I want to get checked friends using checkbox. But I not quite sure how i will achieve it, hope someone can help me.
This is my state:
state = {checked: false}
This is where I want to map array
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<Text>{name}</Text>
<CheckBox
checked={this.state.checked}
onPress={(val)=>{}}
/>
</View>))}
Note: Or Could someone write me an app/code snippet in snack.expo.io how to get only checked checkbox value
You can write a custom checkbox component
export default class CustomCheckbox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
}
toggleChange(){
this.setState({checked: !this.state.checked});
}
render() {
return (
<View>
<Text>{this.props.name}</Text>
<CheckBox
checked={this.state.checked}
onPress={() => this.bind.toggleChange(this)}
/>
</View>
);
}
}
and import your CustomCheckbox component
import CustomCheckbox from "your CustomCheckbox.js path"
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<CustomCheckbox name={name} />
</View>
))}
Your code is pretty good to go, you just need to update a bit. You have following two options:
Your friend's array should have checked key within each containing object, then you can simply do something like this.
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name, checked} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={checked}
onPress={(val)=>{}}
/>
</View>))
}
Other is you to save the name of the person as key and true/false as the checked state, eg :
toggleCurrentFirendState = (item)=>{
this.setState((prevState)=>{
let {name} = item //get name from clicked friend from the list
return {
...prevState, //used spread operator, so that other states doesn't get mutat.
[name]:!prevState[name] //toogle state of clicked item
}
})
}
//within your render
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={name ===this.state[name]} //see change
onPress={(val)=>{this.toggleCurrentFirendState(item)}}
/>
</View>))
}

Update Input Value while mapping React Native

I am creating react-native mobile app. I have an array with some values. I want to set array's value into input field. I have added value in the fields but i can't able to update these values. I have set my values in a qty variable like this:
constructor(props) {
super(props);
this.state = {
qty:[],
}
}
componentWillMount() {
var ids = [];
this.props.data.map((dataImage,Index) => {
dataImage['pro-name'] != undefined && (
ids.push({'qty':dataImage['pro-qty']})
)
})
this.setState({qty:ids})
}
render() {
return {
this.props.data.map((dataImage,Index)=>(
<View key={Index} style={productStyle.cartview}>
{dataImage['pro-name'] && (
<View style={productStyle.cartmain}>
<Input value={this.state.qty[Index]['qty']} onChange={this.handleChangeInput.bind(this)} style={{width:40,height:40,textAlign:'left'}} />
</View>
)}
</View>
))
}
}
Its showing values properly into the input field but i can't able to type anything into the field to update the values. what can i do for this
I will suggest you to move your input container into separate class, its better approach and each component will handle its own state. Its easy to handle and will result better in performance too.
components = []
render() {
return this.props.data.map((item, index) => (
<CustomComponent data={item} index={index} ref={(ref) => this.components[index] = ref} />
))
}
You can then get child (CustomComponent) value from its ref.
this.components[index].getValue()
this.components[index].setValue('Value');
You will need to create these functions (getValue & setValue) in CustomComponent class.
solution
Here is solution to your query. You need to install lodash or find other solution to make a new copy qty.
<Input onChangeText={(text) => this.handleChangeText(text, index)} />
handleChangeText = (text, index) => {
const qty = _.cloneDeep(this.state.qty);
qty[index] = {
qty: text
}
this.setState({qty})
}
Your Input's value is set to this.state.qty[Index]['qty']. And to change it on text edit, you can do it like this. You do not need to bind the function, instead use an arrow function like this.
onChangeText={ (newValue) => {
this.setState({ <your-input-value-state>:newValue })
}}
You have to update the value of each Input individually on onChange event.
Replace your with Input with this
<Input value={this.state.qty[Index]['qty']}
onChange={this.handleChangeInput.bind(this, Index)}
style={{width:40,height:40,textAlign:'left'}}
/>
and update the state accordingly with the Index when the event is called
handleChangeInput(index, value){
let {qty} = this.state;
let qty_update = qty.slice();
qty_update[index]['qty'] = value;
this.setState({qty: qty_update});
}

How to set state on child component?

I have a component that renders out a list of buttons, lets call this 'ButtonList'. When one of the buttons is clicked, the event is bubbled up like so:
<ButtonList onButtonPressed={(mins) => { console.log(mins); }} />
In response to this, I want to hide that ButtonList and show another component that is currently hidden. The ButtonList has some state such as "state { visible: true }" that I want to toggle that stops it rendering. How do I make a call to toggle the state of that ButtonList and then also call my other component in this view to also toggle its visible state to show?
Thanks.
swappingComponentsExample = () => {
return (
<View>
{this.state.showButtonList ? (
<ButtonList
onButtonPressed={mins => {
this.setState({showButtonList: false});
console.log(mins);
}}
/>
) : (
<OtherComponent />
)}
</View>
);
};
// Renders both components but passes style override to hide the object
// ButtonList/OtherComponent are not destroyed and recreated using this method
hidingExample = () => {
return (
<View>
<ButtonList
onButtonPressed={mins => {
this.setState({showButtonList: false});
console.log(mins);
}}
style={!this.state.showButtonList && {display: 'none'}}
/>
<OtherComponent
style={this.state.showButtonList && {display: 'none'}}
/>
</View>
);
};