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

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)}
/>

Related

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

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>
);
}
}

firestore document id will be undefined after I update array

I have this flatlist which receive data from firestore and send as props to projectsummery.js
const ProjectList =({projects})=> {
return(
<FlatList
data={projects}
renderItem={(project,index)=>{
return(
<ProjectSummery project={project} key={project.item.id}
//keyExtractor={(item, index) =>item.id}/>
)
} }
/>
)
}
Here I have a button which which sends document id which is something like this
{project.item.id} == CSmet3tRjpjDcJ437M78
ProjectSummery.js
const ProjectSummery =(props)=> {
const {project,auth}=props
return(
<>
<View >
<Text> {project.item.title} </Text>
<Text>likes { project.item.likes.length}</Text>
<Text>{project.item.id}</Text>//document id in in firestore
<View>
<Button title='like' onPress{()=>props.likesPosts(project.item.id)}/>
</View>
</View>
const mapDispatchToProps=(dispatch)=>{
return{
likePosts:(postId)=>dispatch(likePosts(postId))
}
}
When I try to update array in firebase the first time it work but the second time the document id will be undefined. I use React-Native. Thanks for help...
export const likePosts = (postId) => {
return (dispatch,getState,{getFirebase,getFirestore})=>{
const profile=getState().firebase.profile
const authId=getState().firebase.auth.uid
const firestore=getFirestore()
firestore.collection('projects').doc(postId).update({
//this postId will be be undefined in the 2nd time
likes:firestore.FieldValue.arrayUnion({
likedAt:new Date(),
likedBy:authId,
name: profile.firstName
})
})
}}
The fist update postId == CSmet3tRjpjDcJ437M78 in the 2nd time postId will be undefined
What's happening is that when you click a like button the first time, it's working as expected so it gets the proper postId and then continues with the process you have defined. However, when you try the 2nd time it fails to fetch the postId as it's already liked.
The idea is that you'll need to either define an if statement and specify what should happen if it's already clicked and it get's clicked again (possibly storing the postId somewhere the first time and using it from there), or make an initial check that returns a specific message to the user if it's already clicked.
The issue has nothing to do with Firestore itself but with the button and the states of liked/unliked.
Here is one nice interactive example on codepen.io of a proper way of building like buttons using react. React Like Button
HTML
<div id="example"></div>
CSS
.btn-primary {
background-color: #23aa4e;
border-color: #177d37;
}
#example {
margin: 3rem;
}
.customContainer {
border: 1px solid black;
}
JS
class LikeButton extends React.Component {
constructor() {
super();
this.state = {
liked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
liked: !this.state.liked
});
}
render() {
const text = this.state.liked ? 'liked' : 'haven\'t liked';
const label = this.state.liked ? 'Unlike' : 'Like'
return (
<div className="customContainer">
<button className="btn btn-primary" onClick={this.handleClick}>
{label}</button>
<p>
you {text} this. Click to toggle.
</p>
</div>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
)

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 reload flat list in React-Native?

I am switching from android to react native. Complete naive.
I wanted to implement something like recyclerview in react native and found out about FLATLIST
Now the problem is initially my data variable is empty and later on i am adding data into that variable. Now how do i notify the flat list that the data has changed and it should now re render itself.
Like in recyclerview we use adapter.notifyDataSetChanged(); to inform the recycler view about the change that it should re-render itself now
The code i am using is
export default class Convo extends Component{
constructor(props){
super(props);
this.state = {
loggedIn: 'false',
title: 'Login/SignUp',
messages: []
};
this.downloadConversation = this.downloadConversation.bind(this);
}
componentDidMount(){
this.downloadConversation();
}
downloadConversation(){
this.setState(
{message: [
{
key: "HIHI",
name: "lets this bullshit",
message: "I i i"
},
{
key: "HIHI2",
name: "lets change bullshit",
message: "I i i"
},
{
key: "HIHI3",
name: "lets change this ",
message: "I i i"
}
]}
);
//After updating the messages object, i want to notify flat list about
//the change, basically i will be updating this object asynchronously
// in background which will take time. for now i am updating directly
//to show you
}
renderFlatListItem(item) {
return (
<View key={item.key} style={styles1.flatviewItem}>
<Text>User1</Text>
<Text style={styles1.name}>{item.name}</Text>
<Text style={styles1.messageStyle}>{item.message}</Text>
</View>
)
}
render(){
return(
<View style={styles1.container}>
<View style={styles1.header}>
<Text style={styles1.h2style}>Conversation List</Text>
</View>
<FlatList
style={styles1.flatview}
extraData={this.state}
keyExtractor={item=>item.key}
showsVerticalScrollIndicator={true}
data={this.state.messages}
renderItem={({item}) => this.renderFlatListItem(item)}
/>
</View>
);
}
}
Your component should automatically re-render when the component state changes (if anything in your render method references the changed piece of state). I think you just need to change 'message' to 'messages' when you setState in your downloadConversation() method. Your FlatList is looking for this.state.messages, not this.state.message and this.state.messages is never changed. Just fix that typo & hopefully that fixes it.

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!