I'm wondering if you could help me fix the following issue. Using the native-base datepicker I am trying to manipulate the default date. On load from the parent component I am setting it to today's date. Then using some logic to add x number of months. The date is then updated in the state, but it's not being mapped to the datepicker. See code below:
// Parent state from parent component
state = {
index: 0,
routes: [
{ key: '1', title: 'STEP 1' },
{ key: '2', title: 'STEP 2' },
{ key: '3', title: 'STEP 3' },
{ key: '4', title: 'STEP 4' }
],
purposes: [],
purpose_Of_Examination_Id: 0,
colours: [],
colour_Id: 0,
first_Examination: false,
installed_Correctly: null,
defect_Reason_Id: 0,
defect_Reasons: [],
hasDefects: false,
defect: '',
inspected_At: moment().toDate(),
next_Inspection_Date: moment().toDate()
}
// Child component
constructor(props: any) {
super(props);
this.state = this.props.parentState;
}
async componentDidMount() {
this.bindDates();
}
bindDates() {
var inspected_At = moment().toDate();
var next_Inspection_Date = moment().toDate();
var safe_For_Use = false;
if (this.props.inspection.hasDefects == true) {
next_Inspection_Date = moment().toDate();
safe_For_Use = false;
} else {
next_Inspection_Date = moment(inspected_At).add(this.props.equip.inspection_Interval, 'M').toDate();
safe_For_Use = true;
}
this.setState({inspected_At: inspected_At, next_Inspection_Date: next_Inspection_Date, safe_For_Use: safe_For_Use}, () => {
console.log("NEXT INSPECTION DATE state: " + this.state.next_Inspection_Date);
});
}
// Part of view
<View style={styles.nextInspectionDateContainer}>
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Next Inspection Date:</Text>
<DatePicker
locale="en_GB"
defaultDate={this.state.next_Inspection_Date}
formatChosenDate={date => { return moment(date).format('DD/MM/YYYY'); }}
onDateChange={(date) => { this.setState({ next_Inspection_Date: date }) }}
/>
</View>
</View>
This is pretty common (:
this.setState() is async so you will need to return a promise from this.setState() to a function which will update your DatePicker instead of just logging them. Then you can call forceupdate() to show your updates.
if you were using stateless functional components, it will be much better as you would only need a simple Hook to get all the above done.
keep in mind from docs:
Normally you should try to avoid all uses of forceUpdate() and only
read from this.props and this.state in render().
Related
I'm working on a react-admin page where I want to display some filters depending on the values of another one. To do so, I'm using the filterValues from the ListContext and simply show/hide the dependant filters:
const PostFilter = (props) => {
const { filterValues } = useListContext();
const { mainType } = filterValues;
return (
<Filter {...props}>
<SelectInput
source="mainType"
alwaysOn
choices={[
{ id: "type_1", name: "Type 1" },
{ id: "type_2", name: "Type 2" },
{ id: "type_3", name: "Type 3" }
]}
/>
{mainType === "type_1" && (
<SelectInput
source="type1"
alwaysOn
choices={[
{ id: "type_1_a", name: "A" },
{ id: "type_1_b", name: "B" },
{ id: "type_1_c", name: "C" }
]}
/>
)}
{mainType === "type_2" && (
<SelectInput
source="type2"
alwaysOn
choices={[
{ id: "type_2_a", name: "A" },
{ id: "type_2_b", name: "B" },
{ id: "type_2_c", name: "C" }
]}
/>
)}
</Filter>
);
};
This visually works fine but if I choose a value in one of the child filter and then change the main filter, the url is still containing the filter value even though it's not on screen anymore. As the url contains a wrong filter, then the list is not filtered properly.
To workaround this, I played with a useEffect and the setFilters function but it feels super hacky:
const { filterValues, displayedFilters, setFilters } = useListContext();
const { mainType } = filterValues;
// we need this workaround because these 3 values are not stable
// and get be used as the effect dependencies. However, we still want to be sure
// to always reference their latest version
const refs = React.useRef({ filterValues, displayedFilters, setFilters });
React.useEffect(() => {
refs.current = { filterValues, displayedFilters, setFilters };
});
React.useEffect(() => {
const filters = { ...refs.current.filterValues };
if (mainType !== "type_1") {
delete filters.type1;
}
if (mainType !== "type_2") {
delete filters.type_2;
}
refs.current.setFilters(filters, refs.current.displayedFilters);
}, [mainType]);
Here's a sandbox showing the result: https://codesandbox.io/s/hidden-mountain-i62p3?file=/src/posts.js
Is there a better way to achieve what I want?
I am trying to create a chat feature in my react native app. I am using react-native-gifted-chat and saving the messages in firestore. Here is the behavior that is occurring:
When I send a message, ALL the messages re render, some of them are duplicates, as you can see I only have 3 messages sent so far, but all these duplicates are making me wonder why the entire thing is re-rendering and why there are duplicates when it does re-render.
The code:
class Chat extends React.Component {
constructor(props) {
super(props)
this.state = {
messages: [],
currentUser: null,
isLoading: true,
messageID: ""
}
}
//---------------------------------------------------------------
async componentDidMount (){
// get user info from firestore
let userUID = Firebase.auth().currentUser.uid
await Firebase.firestore().collection("users").doc(userUID).get()
.then(doc => {
data = doc.data()
this.setState({
currentUser: {
name: data.username,
avatar: data.profilePic,
_id: doc.id,
},
})
})
const messages = []
await Firebase.firestore().collection("chat")
.orderBy("createdAt", "desc")
.limit(50)
.onSnapshot(querySnapshot => {
querySnapshot.forEach((res) => {
const {
user,
text,
createdAt,
} = res.data();
messages.push({
key: res._id,
user,
text,
createdAt,
});
})
this.setState({
messages,
isLoading: false,
});
})
}
//Load 50 more messages when the user scrolls
//
//Add a message to firestore
onSend = async(message) => {
await Firebase.firestore().collection("chat")
.add({
user: {
_id: this.state.currentUser._id,
name: this.state.currentUser.name,
avatar: this.state.currentUser.avatar,
},
})
.then(ref => this.setState({messageID: ref.id}))
await Firebase.firestore().collection("chat")
.doc(this.state.messageID)
.set({
_id: this.state.messageID,
text: message[0].text,
createdAt: message[0].createdAt
}, { merge: true })
}
render() {
if(this.state.isLoading){
return(
<View style = {{backgroundColor: '#000000', flex: 1}}>
<ActivityIndicator size="large" color="#9E9E9E"/>
</View>
)
}
return (
<View style={{backgroundColor: '#000000', flex: 1}}>
<GiftedChat
showUserAvatar={true}
renderUsernameOnMessage={true}
messages={this.state.messages}
onSend={message => this.onSend(message)}
scrollToBottom
/>
</View>
)
}
}
Some notes:
Every time the component mounts, the messages array pushes the messages to the state array.
The component mounts when I send a message, thus re-rendering the array of messages
Each message ID is unique and generated by firebase using "Add"
Let me know how I can fix this issue! thanks
Duplication is because of just single line
const messages = []
Move this line inside listener, i.e.onSnapShot()
await Firebase.firestore().collection("chat")
.orderBy("createdAt", "desc")
.limit(50)
.onSnapshot(querySnapshot => {
const messages = []
// rest of your code which is having forEach loop
});
The issue was that messages object was created only once when the component loaded, and you were pushing elements to that object only.
I am making a react native application in which i need to make checkbox during runtime.I means that from server i will get the json object which will have id and label for checkbox.Now i want to know that after fetching data from server how can i make checkbox also how can i handle the checkbox , i mean that how many number of checkbox will be there it will not be static so how can i declare state variables which can handle the checkbox.Also how can i handle the onPress event of checkbox.Please provide me some help of code .Thanks in advance
The concept will be using an array in the state and setting the state array with the data you got from the service response, Checkbox is not available in both platforms so you will have to use react-native-elements. And you can use the map function to render the checkboxes from the array, and have an onPress to change the state accordingly. The code will be as below. You will have to think about maintaining the checked value in the state as well.
import React, { Component } from 'react';
import { View } from 'react-native';
import { CheckBox } from 'react-native-elements';
export default class Sample extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{ id: 1, key: 'test1', checked: false },
{ id: 2, key: 'test1', checked: true }
]
};
}
onCheckChanged(id) {
const data = this.state.data;
const index = data.findIndex(x => x.id === id);
data[index].checked = !data[index].checked;
this.setState(data);
}
render() {
return (<View>
{
this.state.data.map((item,key) => <CheckBox title={item.key} key={key} checked={item.checked} onPress={()=>this.onCheckChanged(item.id)}/>)
}
</View>)
}
}
Here's an example how you can do this. You can play with the code, to understand more how it's working.
export default class App extends React.Component {
state = {
checkboxes: [],
};
async componentDidMount() {
// mocking a datafetch
setTimeout(() => {
// mock data
const data = [{ id: 1, label: 'first' }, { id: 2, label: 'second' }];
this.setState({
checkboxes: data.map(x => {
x['value'] = false;
return x;
}),
});
}, 1000);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
{JSON.stringify(this.state)}
</Text>
{this.state.checkboxes.length > 0 &&
this.state.checkboxes.map(checkbox => (
<View>
<Text>{checkbox.label}</Text>
<CheckBox
onValueChange={value =>
this.setState(state => {
const index = state.checkboxes.findIndex(
x => x.id === checkbox.id
);
return {
checkboxes: [
...state.checkboxes.slice(0, index),
{ id: checkbox.id, label: checkbox.label, value },
...state.checkboxes.slice(index+1),
],
};
})
}
value={checkbox.value}
key={checkbox.id}
/>
</View>
))}
</View>
);
}
}
So I am making a music player and used react-native-video. For my output, I am able to play all the songs that I added it into the App.js where the data is Track. But I am able to change songs but not able to shuffle the music player when I press the forward button. Even when I toggled it on. I am not my error that does not allow me to shuffle them.
This is my Player Component
import Controls from './Controls';
import Video from 'react-native-video';
export default class Player extends Component {
constructor(props) {
super(props);
this.state = {
paused: true,
totalLength: 1,
currentPosition: 0,
selectedTrack: 0,
repeatOn: false,
shuffleOn: true,
};
}
setDuration(data) {
// console.log(totalLength);
this.setState({totalLength: Math.floor(data.duration)});
}
setTime(data) {
//console.log(data);
this.setState({currentPosition: Math.floor(data.currentTime)});
}
seek(time) {
time = Math.round(time);
this.refs.audioElement && this.refs.audioElement.seek(time);
this.setState({
currentPosition: time,
paused: false,
});
}
onBack() {
if (this.state.currentPosition < 10 && this.state.selectedTrack > 0) {
this.refs.audioElement && this.refs.audioElement.seek(0);
this.setState({ isChanging: true });
setTimeout(() => this.setState({
currentPosition: 0,
paused: false,
totalLength: 1,
isChanging: false,
selectedTrack: this.state.selectedTrack - 1,
}), 0);
} else {
this.refs.audioElement.seek(0);
this.setState({
currentPosition: 0,
});
}
}
onForward() {
if (this.state.selectedTrack < this.props.tracks.length - 1) {
this.refs.audioElement && this.refs.audioElement.seek(0);
this.setState({ isChanging: true });
setTimeout(() => this.setState({
currentPosition: 0,
totalLength: 1,
paused: false,
isChanging: false,
selectedTrack: this.state.selectedTrack + 1,
}), 0);
}
}
render() {
const track = this.props.tracks[this.state.selectedTrack];
const video = this.state.isChanging ? null : (
<Video source={{uri: track.url}} // Can be a URL or a local file.
ref="audioElement"
paused={this.state.paused} // Pauses playback entirely.
resizeMode="cover" // Fill the whole screen at aspect ratio.
repeat={false} // Repeat forever.
onLoadStart={this.loadStart} // Callback when video starts to load
onLoad={this.setDuration.bind(this)} // Callback when video loads
onProgress={this.setTime.bind(this)} // Callback every ~250ms with currentTime
onEnd={this.onEnd} // Callback when playback finishes
onError={this.videoError} // Callback when video cannot be loaded
style={styles.audioElement} />
);
return (
<View style={styles.container}>
<Controls
onPressRepeat={() => this.setState({repeatOn : !this.state.repeatOn})}
repeatOn={this.state.repeatOn}
shuffleOn={this.state.shuffleOn}
forwardDisabled={this.state.selectedTrack === this.props.tracks.length - 1}
onPressShuffle={() => this.setState({shuffleOn: !this.state.shuffleOn})}
onPressPlay={() => this.setState({paused: false})}
onPressPause={() => this.setState({paused: true})}
onBack={this.onBack.bind(this)}
onForward={this.onForward.bind(this)}
paused={this.state.paused}/>
{video}
</View>
);
}
}
This is my CSS
const styles = {
container: {
flex: 1,
backgroundColor: 'rgb(4,4,4)',
},
audioElement: {
height: 0,
width: 0,
}
};
And this is my App.js Component
import React, { Component } from 'react';
import Player from './Player';
export const TRACKS = [
{
id: '1',
url: 'http://tegos.kz/new/mp3_full/Post_Malone_-_Better_Now.mp3',
title: 'Better Now',
artist: 'Post Malone',
artwork: 'https://upload.wikimedia.org/wikipedia/en/thumb/c/c1/Beerbongs_%26_Bentleys_by_Post_Malone.png/220px-Beerbongs_%26_Bentleys_by_Post_Malone.png'
},
{
id: '2',
url: 'http://tegos.kz/new/mp3_full/Luis_Fonsi_feat._Daddy_Yankee_-_Despacito.mp3',
title: 'Despacito',
artist: 'Luis Fonsi',
artwork:'https://upload.wikimedia.org/wikipedia/en/c/c8/Luis_Fonsi_Feat._Daddy_Yankee_-_Despacito_%28Official_Single_Cover%29.png'
},
{
id: '3',
url: 'http://tegos.kz/new/mp3_full/Clean_Bandit_-_Solo_(feat._Demi_Lovato)_(Latin_Remix).mp3',
title: 'Solo (Latin Remix)',
artist: 'Clean Bandit feat. Demi Lovato',
},
{
id: '4',
url: 'http://tegos.kz/new/mp3_full/Greyson_Chance_-_Waiting_Outside_The_Lines.mp3',
title: 'Waiting Outside The Lines',
artist: 'Greyson Chance',
artwork: ''
},
];
export default class App extends Component {
render() {
return <Player tracks={TRACKS} />
}
}
If you got autoPlay variable(I got that in redux) , then just check below condition for auto play.
if(this.props.autoPlay){
if( this.state.currentPosition >= this.state.totalLength ){
this.onForward();
}
}
I am new to React Native and trying to build a Messenger app and I have 2 components Search and Messenger​. I am struggling to pass the data I got from Search to Messenger.
Search component finds user (receiver) and me being sender I want to communicate but after finding user in Search I want to pass that user to Messenger so that I can chat with that specific user that found in <Search> component.
In addition, Search component has Views that will display user calendar etc.. so ideally I don't want to use <Messenger> in render() method of Search as it will include Messenger component features inside the Search component which destroys the purpose of <Search> component.
So my code is :
'use strict';
var Search = React.cerateClasss({
getDefaultProps: function () {
return {
date: new Date(),
singerName:''
};
},
getInitialState: function () {
return {
date: this.props.date,
artistName: '',
artistUserId: 1,
maxNoArtist: 0,
imagePath: '../common/images/1.png',
user: null
}
},
getArtistName: function () {
var artist = [];
var query = new Parse.Query(Parse.User);
query.equalTo('userId', this.state.artistUserId);
return query.first({
success: (result) => {
this.setState({artistName: result.get('name')});
this.props.singerName= result.get('name');
this.setState({imagePath: result.get('image').url()});
},
error: (data, error) => {
console.log('Error occured : ' + error.message())
}
});
},
render: function () {
if (!this.state.user) {
return <View style={styles.container}>
<Text style={styles.label}> Loading.... </Text>
</View>
}
var username = this.state.user.get('username');
return (
<View style={styles.container}>
<ResponsiveImage source={{uri:this.state.imagePath}} initHeight="200" initWidth="400"/>
<Text style={styles.label}>
{this.state.artistName}
</Text>
<View style={styles.innerButtonView}>
<Button text={'Onki'} onPress={this.getPreviousArtistName}/>
<Button text={'Indiki'} onPress={this.getNextArtistName}/>
</View>
<CalendarPicker
selectedDate={this.state.date}
onDateChange={this.onDateChange}
/>
<View style={styles.innerButtonView}>
<Button text={'Cyk'} onPress={this.onLogoutPress}/>
<Button text={'Habarlas'} onPress={this.onPress}/>
</View>
<Messenger singerName={this.props.singerName}></Messenger> // BREAKS SEARCH COMPONENT PURPOSE - INCLUDES MESSENGER FEATURES IN TO SEARCH COMPONENT
</View>
);
},
})
var Messenger = React.createClass({
getInitialState: function () {
return {
greeting: 'Salam',
date: new Date(),
errorMessage: '',
user: null,
olderMessageTextFrom: [],
olderMessageTextTo: [],
olderMessageDateFrom: [],
olderMessageDateTo: [],
earlierMessages: []
}
},
componentWillMount: function () {
Parse.User.currentAsync().then((user) => {
this.setState({user: user})
}
)
},
getMessages() {
return [
{
text: this.state.greeting,
name: this.props.singerName,
image: require('../common/images/1.png'),
position: 'left',
date: new Date()
},
I am late to answer but I did in different way using props.
I have two components.
Splash.js
Home.js
I am passing the data (Let's take String) from Splash.js to Home.js.
First component (Sender)
this.props.navigation.navigate('Home', {user_name: userName})
Second component (Receiver)
this.props.navigation.state.params.user_name
Hope this would help you.
OK, so based on your infos, I think the issue is that you don't get the singerName in the Messenger component.
First, I'd change your getArtistName method to this :
getArtistName: function () {
var artist = [];
var query = new Parse.Query(Parse.User);
query.equalTo('userId', this.state.artistUserId);
return query.first({
success: (result) => {
this.setState({artistName: result.get('name')});
// Removed the this.props.singerName = ...
this.setState({imagePath: result.get('image').url()});
},
error: (data, error) => {
console.log('Error occured : ' + error.message())
}
});
}
then in your render method :
<Messenger singerName={this.state.artistName} />
Inside a component you need to use setState and not change props :
that is to say that this.props.singerName = 'singer' is a wrong way of doing things, you should do this.setState({singerName: 'singer'}); then access it with this.state.singerName
Inside your messenger component, you access it with this.props.singerName