React-Native-Material-Dropdown not showing data on Front-end - react-native

I have used react-native material dropdown to fetch data from my API as follows:
<Dropdown
label='colors'
data={this.state.data.colors}
containerStyle={{width: 50}}
/>
{console.log("sbHASB",this.state.data.colors)}
However when I implement thi, I do get the colors on my log but they do not seem to appear on the list, it seems to be blank, can anyone please tell me why is ot so?
Any help would be great, thank you.
my logs after implementing are as follows:
sbHASB ["Blue", "White", "Blue", "White", "Blue", "White", "Blue", "White"]
Do tell me if you require anything else.

Assuming you are using react-native-material-dropdown, the documentation on their github suggests that the data prop should be a list of objects with a value key. See here the example given.
import React, { Component } from 'react';
import { Dropdown } from 'react-native-material-dropdown';
class Example extends Component {
render() {
let data = [{
value: 'Banana',
}, {
value: 'Mango',
}, {
value: 'Pear',
}];
return (
<Dropdown
label='Favorite Fruit'
data={data}
/>
);
}
}
For your list to work you should transform it to match this format, for example
const data = this.state.data.colors.map((color) => ({value: color}))
Given your example above that could look like
<Dropdown
label='colors'
data={this.state.data.colors.map((color) => ({value: color}))}
containerStyle={{width: 50}}
/>
However I would advise transforming the data before this step, for example when you receive the response from the api.
see this example on snack.io, the dropdown will work best if you preview it on a device since the animation doesn't display properly on the web preview.
https://snack.expo.io/#dannyhw/dropdown
UPDATE:
Here is the updated example that includes an example of how it can be used dynamically
export default class App extends React.Component {
state = {
data: {colors: []}
}
getSomeData() {
// imagine this is your api call here and it returns a promise that resolves to be a list of colours
return Promise.resolve(["green", "White", "Blue", "White", "Blue", "White", "Blue", "White"])
}
componentDidMount(){
this.getSomeData().then((result)=> {
this.setState(state => state.data.colors = result)
})
}
render() {
return (
<View style={styles.container}>
<Dropdown
label='colors'
data={this.state.data.colors.map((color) => ({value: color}))}
containerStyle={{width: 150}}
/>
</View>
);
}
}

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

react-native-material-textfield Doesn't work as a controlled input

I'm using "react-native-material-textfield" for text inputs. I have a View to edit user details it fetch values from api when mounting and set it to state. But after upgrading "react-native-material-textfield" to "0.16.1" that original first name value is not shown in the text input after mounting. What I'm doing wrong here ?
constructor(props) {
super(props);
this.state = {
firstName: '',
};
}
componentDidMount(props) {
APIcall().then(data)=>{
this.setState({
firstName: data.firstName
});
}
}
<TextField
label="First Name"
value={this.state.firstName}
onChangeText={firstName => this.setState({firstName})}
/>
I ran into this after upgrading. In version 0.13.0 of the library, it switched to being a fully uncontrolled component according to the release notes.
Changed
defaultValue prop becomes current value on focus
value prop provides only initial value
Based on the current usage docs, there is now a method exposed for setting & getting the value using a ref to the component:
let { current: field } = this.fieldRef;
console.log(field.value());
(Personally, while I can maybe understand this improving performance because typing can often be fast for state updates, I'm not a fan of uncontrolled components since I want my state to drive the UI. I feel like this makes other live updates for validation very fiddly.)
In react-native-material-textfield, 'value' prop acts as default. To update the value you need to use ref. Get the ref using React.createRef(), then use setValue function
import React, { Component } from 'react';
import { TextField } from 'react-native-material-textfield';
import { View, Button } from 'react-native';
export default class TestComponent extends Component {
textField = React.createRef<TextField>();
constructor(props) {
super(props);
this.state = {
value: 'check',
};
}
onChangeText = () => {
// Send request via API to save the value in DB
};
updateText = () => {
if (this.textField && this.textField.current) {
this.textField.current.setValue('test');
}
};
render() {
return (
<View>
<TextField
label="Test value"
value={this.state.value}
onChangeText={this.onChangeText}
ref={this.textField}
/>
<Button onPress={this.updateText} />
</View>
);
}
}
Touch area in TextView
https://github.com/n4kz/react-native-material-textfield/issues/248
react-native-material-textfield
labelTextStyle={{ position: 'absolute', left: '100%' }}
label: {
fontFamily: fonts.Muli_SemiBold,
fontSize: 14,
letterSpacing: 0.1,
color: colors.gray90,
position: 'absolute', left: '100%'
},
<TextField
style={style.textInputRight}
labelTextStyle={style.label}
labelFontSize={16}}
onChangeText={value => onTextChange(value)}
/>

VideoJS overlay and React

I was wondering, is it possible to add a react component as the content?
I added the component inside the overlay like so -
this.player.overlay({
content: <SomeReactComponent />,
align: 'bottom-left',
overlays: [{
start: 'play',
end: 'end'
}]
});
and the SomeReactComponent is just a react component for a dynamic image renderer that looks like this
import like from './like.png';
import love from './love.png';
import neutral from './neutral.png';
class SomeReactComponent extends Component {
getImage(pic) {
const image = pic;
return image;
}
render() {
const pic = [love, like, neutral];
return (
<div>
{ sentimentsArray.map(sentiment =>
<img src={this.getImage(pic)} style={{ width: '75%', height: '75%', objectFit: 'scale-down' }} />
)}
</div>
);
}
}
When i call this.player.overlay in my console, it says the overlays.options.content is a Symbol of React.element, however, I'm not getting anything as an overlay
It's not possible to use React component for this property unfortunately, but only string or node element. Take a look to the doc for more information.

How to correctly large state updates in React Native?

I am writing a small ReactNative application that allows users to invite people to events.
The design includes a list of invitees, each of which is accompanied by a checkbox used to invite/uninvite said invitee. Another checkbox at the top of the list that performs a mass invite/uninvite on all invitees simultaneously. Finally a button will eventually be used to send out the invites.
Because the state of each of these elements depends changes made by the other I often need to re-render my entire UI whenever the user takes action on one of them. But while this works correctly it is causing me quite a few performance issues, as shown in this video
Here's the code I'm using:
import React, { Component } from 'react';
import { Container, Header, Title,
Content, Footer, FooterTab,
Button, Left, Right,
Center, Body, Text, Spinner, Toast, Root , CheckBox, ListItem, Thumbnail} from 'native-base';
import { FlatList, View } from 'react-native';
export default class EventInviteComponent extends Component {
constructor(props) {
super(props);
console.disableYellowBox = true;
this.state = {
eventName: "Cool Outing!",
invitees:[]
}
for(i = 0; i < 50; i++){
this.state.invitees[i] = {
name: "Peter the " + i + "th",
isSelected: false,
thumbnailUrl: 'https://is1-ssl.mzstatic.com/image/thumb/Purple111/v4/62/08/7e/62087ed8-5016-3ed0-ca33-50d33a5d8497/source/512x512bb.jpg'
}
}
this.toggelSelectAll = this.toggelSelectAll.bind(this)
}
toggelSelectAll(){
let invitees = [...this.state.invitees].slice();
let shouldInviteAll = invitees.filter(invitee => !invitee.isSelected).length != 0
let newState = this.state;
newState = invitees.map(function(invitee){
invitee.isSelected = shouldInviteAll;
return invitee;
});
this.setState(newState);
}
render() {
let invitees = [...this.state.invitees];
return (
<Root>
<Container>
<Content>
<Text>{this.state.eventName}</Text>
<View style={{flexDirection: 'row', height: 50, marginLeft:10, marginTop:20}}>
<CheckBox
checked={this.state.invitees.filter(invitee => !invitee.isSelected).length == 0}
onPress={this.toggelSelectAll}/>
<Text style={{marginLeft:30 }}>Select/deselect all</Text>
</View>
<FlatList
keyExtractor={(invitee, index) => invitee.name}
data={invitees}
renderItem={(item)=>
<ListItem avatar style={{paddingTop: 20}}>
<Left>
<Thumbnail source={{ uri: item.item.thumbnailUrl}} />
</Left>
<Body>
<Text>{item.item.name}</Text>
<Text note> </Text>
</Body>
<Right>
<CheckBox
checked={item.item.isSelected}/>
</Right>
</ListItem>}/>
</Content>
<Footer>
<FooterTab>
<Button full
active={invitees.filter(invitee => invitee.isSelected).length > 0}>
<Text>Invite!</Text>
</Button>
</FooterTab>
</Footer>
</Container>
</Root>);
}
}
In your code, in class method toggelSelectAll() {...} you modify the state directly by using this.state = ..., which is something to be avoided. Only use this.state = ... in your class constructor() {...} to initialize the state, and you should only use this.setState({...}) to update the state anywhere else.
Not sure if this should help your performance issues, but try replacing toggelSelectAll() with the following:
toggelSelectAll() {
const {invitees} = this.state;
const areAllSelectedAlready = invitees.filter(({isSelected}) => !isSelected).length === 0;
this.setState({
invitees: invitees.map(invitee => ({
...invitee,
isSelected: !areAllSelectedAlready
}))
});
}
Good luck! And, let me know if you would like me to refactor your above code to remove the 2nd this.state = ... in your constructor (which, once again, should be avoided when writing React).
I suggest:
Dividing your code by creating multiple components, so you won't have a massive render()
Using Redux to store invitee / global state, so you can choose which components should re-render in case of modifications
That's a good way to learn React Native!

How to loop and render elements in React-native?

Is it possible to loop an identical component in Render function?
Something like this:
...
onPress = () => {
...
};
initialArr = [["blue","text1"],["red","text2"]];
buttonsListArr = [];
for (let i = 0; i < initialArr.length; i++)
{
buttonsListArr.push(
<Button style={{borderColor:{initialArr[i][0]}}} onPress={this.onPress.bind(this)}>{initialArr[i][1]}</Button>
);
}
...
render() {
return (
<View style={...}>
{buttonsListArr}
</View>
)};
I mean this is just finite list of components, so any components like ListView/ScrollView etc is not applicable in this particular case. This is just syntax question.
You would usually use map for that kind of thing.
buttonsListArr = initialArr.map(buttonInfo => (
<Button ... key={buttonInfo[0]}>{buttonInfo[1]}</Button>
);
(key is a necessary prop whenever you do mapping in React. The key needs to be a unique identifier for the generated component)
As a side, I would use an object instead of an array. I find it looks nicer:
initialArr = [
{
id: 1,
color: "blue",
text: "text1"
},
{
id: 2,
color: "red",
text: "text2"
},
];
buttonsListArr = initialArr.map(buttonInfo => (
<Button ... key={buttonInfo.id}>{buttonInfo.text}</Button>
);
render() {
return (
<View style={...}>
{initialArr.map((prop, key) => {
return (
<Button style={{borderColor: prop[0]}} key={key}>{prop[1]}</Button>
);
})}
</View>
)
}
should do the trick
For initial array, better use object instead of array, as then you won't be worrying about the indexes and it will be much more clear what is what:
const initialArr = [{
color: "blue",
text: "text1"
}, {
color: "red",
text: "text2"
}];
For actual mapping, use JS Array map instead of for loop - for loop should be used in cases when there's no actual array defined, like displaying something a certain number of times:
onPress = () => {
...
};
renderButtons() {
return initialArr.map((item) => {
return (
<Button
style={{ borderColor: item.color }}
onPress={this.onPress}
>
{item.text}
</Button>
);
});
}
...
render() {
return (
<View style={...}>
{
this.renderButtons()
}
</View>
)
}
I moved the mapping to separate function outside of render method for more readable code.
There are many other ways to loop through list of elements in react native, and which way you'll use depends on what do you need to do. Most of these ways are covered in this article about React JSX loops, and although it's using React examples, everything from it can be used in React Native. Please check it out if you're interested in this topic!
Also, not on the topic on the looping, but as you're already using the array syntax for defining the onPress function, there's no need to bind it again. This, again, applies only if the function is defined using this syntax within the component, as the arrow syntax auto binds the function.
If u want a direct/ quick away, without assing to variables:
{
urArray.map((prop, key) => {
console.log(emp);
return <Picker.Item label={emp.Name} value={emp.id} />;
})
}