Date Time Picker setState messing up my state - react-native

I have an async function where I load my data and update state:
constructor(props) {
super(props);
this.state = {
userData: {},
isDateTimePickerVisible: false,
};
}
componentDidMount() {
this.getUser();
}
getUser = async () => {
const { navigation } = this.props;
const tenantID = navigation.getParam('userID', '0');
await this.props.getUserByID(tenantID); // Wait for action to complete
this.setState({
userData: this.props.userData
});
};
Then I use the state data to populate the Input value of my render(). However, I added a DateTimePicker to my component where opens the DateTimePicker in the onFocus Input:
<Input
style={styles.valueText}
onFocus={this.showDateTimePicker}
value={getFormattedDate(EndDate)}
/>
<DateTimePicker
isVisible={this.state.isDateTimePickerVisible}
onConfirm={this.handleDatePicked}
onCancel={this.hideDateTimePicker}
date={EndDate}
/>
The methods to handle the show/hide/update date:
showDateTimePicker = () => {
this.setState({
isDateTimePickerVisible: true
});
};
hideDateTimePicker = () => {
this.setState({
isDateTimePickerVisible: false
});
};
handleDatePicked = date => {
const obj = { ...this.state.userData };
obj.LeaseStartDate = date;
this.setState({
userData: obj
});
this.hideDateTimePicker();
};
When I first open the page, it is doesn't have any data in the this.state.userData. But, if I delete the methods 'showDateTimePicker' and 'hideDateTimePicker' the this.state.userData has the data when I first load the page. Why is it happening?
Thanks
Even if I leave the methods without the setState, it does work. However, if I remove these methods it just work.
showDateTimePicker = () => {
};
hideDateTimePicker = () => {
};

Related

How to re-run useQuery and FlatList?

I use FlatList with useState.
const [state, setState] = useState(route);
<FlatList
keyboardDismissMode={true}
showsVerticalScrollIndicator={false}
data={state}
keyExtractor={(comment) => "" + comment.id}
renderItem={renderComment}
/>
When I change the datㅁ which is contained in state, I want to re-run Flatlist with new data.
So after I mutate my data, I try to rerun useQuery first in order to change state. I put refetch module here.
1)
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: route?.params?.photoId,
},
});
If I put button, this onValid function will executed.
<ConfirmButton onPress={handleSubmit(onValid)}>
onValid function changes data and after all finished, as you can see I put refetch().
=> all this process is for that if I add comment and press confirm button, UI (flatlist) should be changed.
const onValid = async ({ comments }) => {
await createCommentMutation({
variables: {
photoId: route?.params?.photoId,
payload: comments,
},
});
await refetch();
console.log(updatePhoto);
};
But when I console.log data after all, it doesnt' contain added data..
what is the problem here?
If you need more explanation, I can answer in real time.
please help me.
add full code
export default function Comments({ route }) {
const { data: userData } = useMe();
const { register, handleSubmit, setValue, getValues } = useForm();
const [state, setState] = useState(route);
const [update, setUpdate] = useState(false);
const navigation = useNavigation();
useEffect(() => {
setState(route?.params?.comments);
}, [state, route]);
const renderComment = ({ item: comments }) => {
return <CommentRow comments={comments} photoId={route?.params?.photoId} />;
};
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: route?.params?.photoId,
},
});
const createCommentUpdate = (cache, result) => {
const { comments } = getValues();
const {
data: {
createComment: { ok, id, error },
},
} = result;
if (ok) {
const newComment = {
__typename: "Comment",
createdAt: Date.now() + "",
id,
isMine: true,
payload: comments,
user: {
__typename: "User",
avatar: userData?.me?.avatar,
username: userData?.me?.username,
},
};
const newCacheComment = cache.writeFragment({
data: newComment,
fragment: gql`
fragment BSName on Comment {
id
createdAt
isMine
payload
user {
username
avatar
}
}
`,
});
cache.modify({
id: `Photo:${route?.params?.photoId}`,
fields: {
comments(prev) {
return [...prev, newCacheComment];
},
commentNumber(prev) {
return prev + 1;
},
},
});
}
};
const [createCommentMutation] = useMutation(CREATE_COMMENT_MUTATION, {
update: createCommentUpdate,
});
const onValid = async ({ comments }) => {
await createCommentMutation({
variables: {
photoId: route?.params?.photoId,
payload: comments,
},
});
await refetch();
console.log(updatePhoto);
};

React Native navigate not updating props

I have one page with a list of "tenants". When I select one tenant if shows the data for this specific tenant. It is working. However, when I navigate back to the tenant list and select another tenant, it does not update the this.props with the new tenant data.
My Tenant Details Page
constructor(props) {
super(props);
this.state = {
tenantData: {}
};
}
componentDidMount() {
this.getTenantID();
}
componentDidUpdate(prevProps) {
// needs to be a unique value
if (prevProps.tenantData.Email !== this.props.tenantData.Email) {
this.getTenantID();
}
}
getTenantID = async () => {
const { navigation } = this.props;
const tenantID = navigation.getParam('tenantID', '0');
await this.props.getTenantByID(tenantID); // Wait for action to complete
this.setState({
tenantData: this.props.tenantData
});
};
My action:
export const getTenantByID = (tID) => {
return (dispatch) => {
axios.get('http://myirent.com/rent/components/iRentApp.cfc', {
params: {
method: 'getTenantByTenant',
tenantID: tID
}
}).then((response) => {
const tenant = response.data.DATA[0];
console.log(tenant);
const getTenant = {
FirstName: tenant[1],
LastName: tenant[2],
Email: tenant[5],
Phone: tenant[6],
Unit: tenant[11],
MiddleName: tenant[3],
RentalAmount: tenant[4],
MoveInDate: getFormattedDate(tenant[7]),
MoveOutDate: getFormattedDate(tenant[8]),
LeaseStartDate: getFormattedDate(tenant[9]),
LeaseEndDate: getFormattedDate(tenant[10])
};
dispatch({
type: GET_TENANT_DATA,
payload: getTenant
});
});
};
};
The tenantID is being updated and the action response data too. It looks like that the page is loading before updating the this.props.tenantData
The componentDidUpdate() is called immediately after the update. This method is not called in the first rendering.
componentDidUpdate(prevProps) {
// typical use cases (don't forget the props comparison)
if (prevProps.navigation !== this.props.navigation) {
const data = this.props.navigation.getParam('tenantID', '0')
this.getTenantID(data);
}
}
getTenantID = async () => {
const { navigation } = this.props;
const tenantID = navigation.getParam('tenantID', '0');
const tenantdata = await this.props.getTenantByID(tenantID); // Wait for action to complete
this.setState({
tenantData: tenantdata,
updateid : tenantID
});
};

Redux not updating this.props in componentDidMount with async method

My redux is not updating the props.
My component:
...
import { connect } from 'react-redux';
import { getTenantByID, updateTenant } from '../actions';
...
constructor(props) {
super(props);
this.state = {
tenantData: {}
};
}
componentDidMount() {
this.getTenant();
}
onChangeText = (text, input) => {
const obj = { ...this.state.tenantData };
obj[input] = text;
this.setState({
tenantData: obj
});
};
onChangeNumberFormat = (text, input) => {
const obj = { ...this.state.tenantData };
let value = parseFloat(text);
if (isNaN(value)) {
value = 0;
}
value = parseFloat(value).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');
obj[input] = value;
this.setState({
tenantData: obj
});
};
getTenant = async () => {
const { navigation } = this.props;
const tenantID = navigation.getParam('tenantID', '0');
await this.props.getTenantByID(tenantID); // Wait for action to complete
this.setState({
tenantData: this.props.tenantData
});
};
...
const mapStateToProps = ({ tenants }) => {
const { error, tenantData, saving } = tenants;
return { error, tenantData, saving };
};
export default connect(mapStateToProps, {
getTenantByID, updateTenant
})(TenantDetails);
In my action, I export the method:
export const getTenantByID = ({ tenantID }) => {
return (dispatch) => {
const getTenant = {
FirstName: 'Jonh', LastName: 'Doe', Email: 'jonh#test.com', Phone: 'xxx-xxx-xxxx',
Unit: '101', MiddleName: '', RentalAmount: '1000.50', MoveInDate: toDate('2019-01-01'),
MoveOutDate: toDate('2019-12-01'), LeaseStartDate: toDate('2019-01-01'), LeaseEndDate: toDate('2019-12-01'),
};
dispatch({
type: GET_TENANT_DATA,
payload: getTenant
});
};
};
And I use the reducer to return the data.
...
const INITIAL_STATE = {
error: false,
data: [],
tenantData: {},
saving: false,
};
...
case GET_TENANT_DATA:
return { ...state, error: false, tenantData: action.payload };
If I do a console.log in the GET_TENANT_DATA in my reducer, I can see that the action.payload has data. But if I do console.log(this.state.tenantData) in my render() method, it is empty. Why is it happening?
Thanks
I include logs in the componentDidMount and render. It display in the following order
call render
this.props.tenantData is empty
Call componentDidMount
this.props.tenantData is empty
call render
this.props.tenantData has value
call render
this.props.tenantData has value
It is never setting state.tenantData. Why is it calling render() after componentDidMount()?
The problem is here, in getTenant function.
getTenant should not be async function becuase you are not returning a promise
componentDidUpdate(prevProps){
if (prevProps.tenantData.Email !== this.props.tenantData.Email) {// you need a unique value to check for changes in props
this.setTenantData();
}
}
setTenantData = () => this.setState({ tenantData: this.props.tenantData });
getTenant = () => {
const { navigation } = this.props;
const tenantID = navigation.getParam('tenantID', '0');
const tenantData = this.props.getTenantByID(tenantID);
};
And this should be your action.
export const getTenantByID = ({ tenantID }) => {
const tenant = {
FirstName: 'Jonh', LastName: 'Doe', Email: 'jonh#test.com', Phone: 'xxx-xxx-xxxx',
Unit: '101', MiddleName: '', RentalAmount: '1000.50', MoveInDate: toDate('2019-01-01'),
MoveOutDate: toDate('2019-12-01'), LeaseStartDate: toDate('2019-01-01'), LeaseEndDate: toDate('2019-12-01'),
};
return {
type: GET_TENANT_DATA,
payload: tenant
};
};
So you can see tenantData under the console.log in componentDidUpdate.
And the reason for setState not working under getTenant is because the component takes time to update after the redux action

react native async getting data when running app first time

I have two components, in first components storing data in asyncstorage, in second component display data, when install app and save data does not get data from asyncstorage, when open app second time data are displayed.
storeData = async (item, messave, messrem) => {
const checkarary = this.state.favorite;
if(checkarary.some(e => e.name === item.name)) {
const value = this.state.favorite;
const position = value.filter((lists) => lists.id !== item.id);
this.setState({
favorite: position
}, () => {
try {
AsyncStorage.setItem('favoriti', JSON.stringify(this.state.favorite), () => {
Toast.show({
text: messrem,
buttonText: "Okay",
duration: 3000,
type: "danger"
});
});
} catch (error) {
}
});
} else {
this.setState({
favorite: [...this.state.favorite, item]
}, () => {
try {
AsyncStorage.setItem('favoriti', JSON.stringify(this.state.favorite), () => {
// AsyncStorage.getItem('favoriti', (err, result) => {
// console.log(result);
// });
Toast.show({
text: messave,
buttonText: "Okay",
duration: 3000,
type: "success"
});
});
} catch (error) {
}
});
}
};
Getting data in second component
_retrieveData = async () => {
try {
AsyncStorage.getItem('favoriti').then((value) => {
const parsed = JSON.parse(value);
this.setState({ favorite: parsed })
})
} catch (error) {
}
};
componentDidMount() {
this._retrieveData();
setTimeout(() => {
this.setState({
loading: false,
})
}, 2000)
};
componentDidUpdate() {
this._retrieveData();
};
How fix this issue, is there some solution. Can I set Item and reload app when install app or somthing else.
Use this
componentWillMount() {
this._retrieveData();
setTimeout(() => {
this.setState({
loading: false,
})
}, 2000)
};
instead of
componentDidMount() {
this._retrieveData();
setTimeout(() => {
this.setState({
loading: false,
})
}, 2000)
};
As componentWillMount is called after constructor is called for class and componentDidMount is called after screen is once rendered.

Pusher chatKit onMessage hook fails in Expo app

I am using React Native with Expo, and I am able to create users + rooms and send messages to them with the following code:
const hooks = {
onMessage: message => {
console.log("message", message);
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
};
class SetupChatKit extends React.Component {
constructor(props) {
super(props);
this.state = {
chatManager: null,
currentUser: {},
currentRoom: {},
messages: [],
usersWhoAreTyping: []
};
}
componentDidMount() {
const { userId, name } = this.props;
this.instantiateChatManager(userId);
this.createChatKitUser({ userId, name });
}
joinOrCreateChatKitRoom = (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
return chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser });
if (mode === "join") {
return currentUser.joinRoom({ roomId: chatKitRoomId, hooks });
}
return currentUser.createRoom({
name: title,
private: false,
hooks
});
})
.then(currentRoom => {
this.setState({ currentRoom });
return currentRoom.id;
})
.catch(error => console.error("error", error));
};
instantiateChatManager = userId => {
const chatManager = new Chatkit.ChatManager({
instanceLocator: "v1:us1:9c8d8a28-7103-40cf-bbe4-727eb1a2b598",
userId,
tokenProvider: new Chatkit.TokenProvider({
url: `http://${baseUrl}:3000/api/authenticate`
})
});
this.setState({ chatManager });
};
My problem is that console.log("message", message); never gets called, even when I manually add messages to the room via the online control panel.
I've tried logging from chatManager, and that looks like the following:
As you can see from the documentation, the onMessage hook needs to be attached on subscribeRoom, not when joining a room.
https://docs.pusher.com/chatkit/reference/javascript#connection-hooks
So probably add subscribeToRoom() after the first success promise in your joinOrCreateChatKitRoom() method.
I refactored the code with async/await and used .subscribetoRoom() like so:
joinOrCreateChatKitRoom = async (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
try {
const currentUser = await chatManager.connect();
this.setState({ currentUser });
let currentRoom;
if (mode === "join") {
currentRoom = await currentUser.joinRoom({
roomId: chatKitRoomId
});
} else {
currentRoom = await currentUser.createRoom({
name: title,
private: false
});
}
this.setState({ currentRoom });
await currentUser.subscribeToRoom({
roomId: currentRoom.id,
hooks: {
onMessage: message => {
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
}
});
return currentRoom.id;
} catch (error) {
console.error("error creating chatManager", error);
}
};