Trying to get setState to work synchronously somehow - reactjs

I am trying to set the state of a variable that is initially populated from an API call...
getInServiceVenues = () =>{
this.setState({ loading: true });
let bodyInService = [];
let iterationInService = 0;
let RequestingNetworkOperatorList = [];
axios.post( '/propertymanagement/listVenues', bodyInService,
{
headers: {}
})
.then( response => {
if (this._isMounted) {
this.setState({loading: false});
this.setState({venues: []});
const venuesInService = response.data.InServiceVenueList;
let venueIdInService = null;
let updatedVenuesInService = [];
bodyInService.push(venuesInService);
bodyInService.forEach(val=>{
venueIdInService = Object.keys(bodyInService[0]);
//console.log(venueId);
})
if(this.state.venuesInService!==[]){
this.setState({venuesInService:[]});
}
venueIdInService.forEach(val=>{
updatedVenuesInService = bodyInService.map(venueInService => {
return {
...venueInService[venueIdInService[iterationInService]],
iterationInService,
RequestingNetworkOperatorList
}
});
this.setState({
venuesInService:[...this.state.venuesInService, updatedVenuesInService]
});
iterationInService = iterationInService + 1;
})
}} )
.catch(error => {
console.log(error);
this.setState({error: true});
});
}
On the click of a button I call this function...
postSelectedHandler = (venues) => {
this.setState({newVenue: true}, () => {
console.log(this.state.newVenue);
});
this.getInServiceVenues();
var event = new Event('input', { bubbles: true });
this.search_venue.dispatchEvent(event);
this.filterList(event);
}
I can only see the updated state if I were to change an input box's value which is why I have the event variable in my function (trying to do an onChange automatically). Any help would be appreciated.
Edited with the render method attached...
render () {
//console.log(this.state.allVenues);
const { isBoxVisible } = this.state;
const {loading } = this.state;
let inServiceVenues = <Alert variant="danger">Something went wrong!</Alert>;
let preAuthVenues = <Alert variant="danger">Something went wrong!</Alert>;
let waitingAuthVenues = <Alert variant="danger">Something went wrong!</Alert>;
let availableVenues = <Alert variant="danger">Something went wrong!</Alert>;
if (!this.state.error) {
inServiceVenues = this.state.filterVenuesInService.map(venueInService => {
return <Venue
key={venueInService[0].iteration}
city={venueInService[0].City}
VenueName={venueInService[0].VenueName}
NetworkOperatorID={venueInService[0].NetworkOperatorID}
ConnectedKeyPools={venueInService[0].connectedKeyPoolList}
clicked={() => this.postSelectedHandler(venueInService.id)} />;
});
preAuthVenues = this.state.filterVenuesPreAuth.map(venuePreAuth => {
return <PreAuthVenues
key={venuePreAuth[0].iteration}
city={venuePreAuth[0].City}
VenueName={venuePreAuth[0].VenueName}
NetworkOperatorID={venuePreAuth[0].NetworkOperatorID}
ConnectedKeyPools={venuePreAuth[0].connectedKeyPoolList}
clicked={() => this.postSelectedHandler(venuePreAuth.id)} />;
});
waitingAuthVenues = this.state.filterVenuesWaiting.map(venueWaitingAuth => {
//console.log(venueWaitingAuth[0].RequestingNetworkOperatorList[0]);
let connectedKeyPoolListNewLine;
if(venueWaitingAuth[0].connectedKeyPoolList!=undefined){
connectedKeyPoolListNewLine = JSON.stringify(venueWaitingAuth[0].connectedKeyPoolList, "<p>").replace(/[[\]"']+/g,'').replace(/,+/g, '\n');
//console.log(connectedKeyPoolListNewLine.replace(/\n,+/g, ''));
}else{
connectedKeyPoolListNewLine = '';
}
return <WaitingAuthVenues
key={venueWaitingAuth[0].iterationWaitingAuth}
city={venueWaitingAuth[0].City}
VenueName={venueWaitingAuth[0].VenueName}
VenueID={venueWaitingAuth[0].awaitingAuthVenueID}
connectedKeyPoolList={connectedKeyPoolListNewLine}
RequestedOperatorList={Object.keys(venueWaitingAuth[0].RequestingNetworkOperatorList)}
AwaitingAuthorizationKeyPool={venueWaitingAuth[0].AwaitingAuthorizationFromKeyPoolOwnerKeyPoolList}
filteredVenuesInService={inServiceVenues}
clicked={() => this.postSelectedHandler(venueWaitingAuth.id)} />;
});
availableVenues = this.state.filterVenuesAvailable.map(venue => {
let connectedKeyPoolListNewLine;
if(venue[0].connectedKeyPoolList!=undefined){
connectedKeyPoolListNewLine = JSON.stringify(venue[0].connectedKeyPoolList, "<p>").replace(/[[\]"']+/g,'').replace(/,+/g, '\n');
//console.log(connectedKeyPoolListNewLine.replace(/\n,+/g, ''));
}else{
connectedKeyPoolListNewLine = '';
}
//console.log(connectedKeyPoolList);
return <AvailableVenues
key={venue[0].iteration}
city={venue[0].City}
VenueName={venue[0].VenueName}
connectedKeyPoolList={connectedKeyPoolListNewLine}
id={venue[0].availableVenueID}
clicked={() => this.postSelectedHandler(availableVenues.id)}
submitted={this.forceUpdateHandler}
getNewData={this.getAvailableVenues}/>;
});
//console.log(this.state.venues[0]);
}

Related

Strange Sockets Behavior with React - [RESOLVED]

I am new to using Web Sockets, and am trying to use the Sockets.io library to finish a chat application I am building. When a message is sent, the recipient receives the message twice, which is obviously not how the app is supposed to work.
I built a Socket context that joins the user into a room that is represented by their unique MongoDB identifier so they can be reached regardless of whichever chat they may be currently viewing. I tried to build it so that each chat the user views enters them into a new room while simultaneously causing them to leave the room for the chat they were viewing previously.
This is my code for the socket-related portion of my server:
io.on('connection', socket => {
socket.on('setup', userData => {
socket.join(userData._id);
});
socket.on('join chat', chatId => {
socket.join(chatId);
});
socket.on('new message', message => {
let chat = message.chat;
chat.users.forEach(user => {
if (user._id === message.sender._id) return;
socket.to(user._id).emit('message received', message);
});
});
socket.on('leave chat', chatId => {
socket.leave(chatId);
});
Here is the relevant code for my socket context (if a new user signs in then it should close the old socket and create a new room representing the new user):
useEffect(() => {
if (!currentUser) return;
const newSocket = io(ENDPOINT);
newSocket.emit('setup', currentUser);
setSocket(newSocket);
return () => newSocket.close();
}, [currentUser]);
And, finally, here is the code within the chat instance component:
useEffect(() => {
if (!socket) return;
socket.emit('join chat', activeChat[0]._id);
return () => socket.emit('leave chat', activeChat[0]._id);
}, [activeChat, socket]);
useEffect(() => {
if (!socket) return;
socket.on('message received', message => {
if (!activeChat[0]._id || message.chat._id !== activeChat[0]._id) {
if (!notifications) return;
setNotifications(prevState => [message, ...prevState]);
return;
} else {
setMessages(prevState => {
return [...prevState, message];
});
}
});
}, [activeChat, fetchChats, notifications, socket, setNotifications]);
Just as a side note, I had the application working previously when I kept the socket instance inside of the chat instance (and did not try importing it from the socket hook), but it inhibited my ability for the user to be contacted while viewing another chat since removed the socket instance when the chat instance unmounted by calling return () => socket.close(). Here is that code:
let socket; // Global scope
useEffect(() => {
socket = io(ENDPOINT);
socket.emit('setup', currentUser);
socket.emit('stop typing', activeChat[0]._id, currentUser);
return () => socket.close();
}, [currentUser, activeChat]);
If there is anything I can clarify, please let me know! Thanks so much for the help :)
EDIT: So I fixed my problem and it had to do with how I was handling the event listeners on the client side, which I was never unmounting. For anyone in the future who faces the same problem, please see the code below that I used to handle user messaging, typing, and handling changes to which users are online. Namaste.
Server.js (relevant portion):
global.onlineUsers = new Map();
io.on('connection', socket => {
socket.on('setup', userId => {
socket.join(userId);
global.onlineUsers.set(userId, socket.id);
for (const [
onlineUserId,
_onlineSocketId,
] of global.onlineUsers.entries()) {
if (onlineUserId === userId) {
socket.emit('logged in user change', [...global.onlineUsers]);
return;
} else {
socket
.to(onlineUserId)
.emit('logged in user change', [...global.onlineUsers]);
}
}
});
socket.on('join room', chatId => {
socket.join(chatId);
});
socket.on('leave room', chatId => {
socket.leave(chatId);
});
socket.on('send-msg', message => {
message.chat.users.forEach(user => {
if (user._id === message.sender._id) return;
socket.to(user._id).emit('msg-received', message);
});
});
socket.on('typing', (room, user) => {
socket.to(room).emit('typing', user.userName);
});
socket.on('stop typing', (room, user) =>
socket.to(room).emit('stop typing', user.userName)
);
socket.on('log out', userId => {
socket.leave(userId);
global.onlineUsers.delete(userId);
for (const [
onlineUserId,
_onlineSocketId,
] of global.onlineUsers.entries()) {
socket
.to(onlineUserId)
.emit('logged in user change', [...global.onlineUsers]);
}
});
});
Socket Context (relevant portion):
useEffect(() => {
if (!currentUser) return;
const newSocket = io(ENDPOINT);
newSocket.emit('setup', currentUser._id);
newSocket.on('logged in user change', users => {
const userIdArr = users.map(([userId, socketId]) => userId);
setOnlineUsers(userIdArr);
});
setSocket(newSocket);
return () => {
newSocket.off('logged in user change');
newSocket.emit('log out', currentUser._id);
};
}, [currentUser]);
Chat Instance (entire component):
import { useCallback, useEffect, useState } from 'react';
import { io } from 'socket.io-client';
import Lottie from 'lottie-react';
import { useChatView } from '../../contexts/chat-view-context';
import Spinner from '../spinner/spinner.component';
import './message-view.styles.scss';
import { useAuthentication } from '../../contexts/authentication-context';
import animationData from '../../animations/typing.json';
import {
defaultToast,
sameSenderAndNotCurrentUser,
TOAST_TYPE,
userSent,
getTyperString,
} from '../../utils/utils';
import { useSocket } from '../../contexts/socket-context';
// Could definitely add timestamp data to the message as well, that would be pretty clean actually
let typingTimer;
const MessageView = () => {
// Somehow we are going to have to get all of the message in a conversation potentially and then mark whether or not they are your messages or someone else's to style accordingly;
const { currentUser } = useAuthentication();
const { activeChat, notifications, setNotifications, fetchChats } =
useChatView();
const { socket } = useSocket();
// const [socketConnected, setSocketConnected] = useState(false);
const [messages, setMessages] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isTyping, setIsTyping] = useState(false);
const [typing, setTyping] = useState(false);
const [typers, setTypers] = useState([]);
// console.log('typers from outside', typers);
// So I am thinking that I can definitely scroll into view whatever message is actually clicked within whatever chat, I don't see why that would not be possible?
// Pretty cool, when the component actually mounts, the ref for the element gets passed into the callback function, could actually do some pretyy coll things with this, like making an animation or shake the screen or bounce the message or anything when the message actually enters the screen...
const handleKeyDown = async e => {
if (!socket) return;
const newMessage = e.target.innerHTML;
if (e.key === 'Enter' && newMessage) {
e.preventDefault();
e.target.innerHTML = '';
try {
const response = await fetch(`http://localhost:4000/api/message`, {
method: 'post',
headers: {
Authorization: `Bearer ${currentUser.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chatId: activeChat[0]._id,
text: newMessage,
}),
});
const message = await response.json();
socket.emit('send-msg', message);
setMessages(prevState => [...prevState, message]);
setTyping(false);
return;
} catch (error) {
defaultToast(TOAST_TYPE.error, 'Error sending');
}
} else {
if (!typing) {
setTyping(true);
socket.emit('typing', activeChat[0]._id, currentUser);
}
const lastTypingTime = new Date().getTime();
const timerLength = 3000;
if (typingTimer) clearTimeout(typingTimer);
typingTimer = setTimeout(() => {
const timeNow = new Date().getTime();
const timeDiff = timeNow - lastTypingTime;
if (timeDiff >= timerLength) {
socket.emit('stop typing', activeChat[0]._id, currentUser);
setTyping(false);
}
}, timerLength);
}
};
const fetchMessages = useCallback(async () => {
if (!socket) return;
if (!activeChat) return;
setIsLoading(true);
const response = await fetch(
`http://localhost:4000/api/message/${activeChat[0]._id}`,
{
method: 'get',
headers: { Authorization: `Bearer ${currentUser.token}` },
}
);
const messages = await response.json();
setMessages(messages);
setIsLoading(false);
}, [activeChat, currentUser.token, socket]);
useEffect(() => {
fetchMessages();
}, [fetchMessages, activeChat]);
useEffect(() => {
if (!socket) return;
socket.emit('join room', activeChat[0]._id);
socket.emit('stop typing', activeChat[0]._id, currentUser);
return () => socket.emit('leave room', activeChat[0]._id);
}, [activeChat, socket, currentUser]);
useEffect(() => {
if (!socket) return;
socket.on('msg-received', message => {
if (!activeChat[0]._id || message.chat._id !== activeChat[0]._id) {
setNotifications(prevState => [message, ...prevState]);
return;
} else {
setIsTyping(false);
setMessages(prevState => [...prevState, message]);
}
});
return () => socket.off('msg-received');
}, [socket, activeChat, setNotifications]);
useEffect(() => {
if (!socket) return;
socket.on('typing', typer => {
setIsTyping(true);
setTypers(prevState => [...new Set([typer, ...prevState])]);
});
socket.on('stop typing', userName => {
const usersStillTyping = typers.filter(typer => typer !== userName);
if (usersStillTyping.length > 0 && typers.length !== 0) {
setIsTyping(true);
setTypers(usersStillTyping);
return;
}
setIsTyping(false);
setTypers([]);
});
return () => {
socket.off('typing');
socket.off('stop typing');
};
}, [socket, typers]);
const setRef = useCallback(
node => {
if (node && isTyping && isScrolledIntoView(node)) {
node.scrollIntoView({ smooth: true });
} else if (node && !isTyping) {
node.scrollIntoView({ smooth: true });
}
},
[isTyping]
);
function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
// Only completely visible elements return true:
var isVisible = elemTop >= 0 && elemBottom <= window.innerHeight;
// Partially visible elements return true:
//isVisible = elemTop < window.innerHeight && elemBottom >= 0;
return isVisible;
}
// What is the best way to make it so that the text bubble can expland if it needs to??
return (
<div className="message-view-container">
{isLoading ? (
<Spinner type="search" />
) : (
<>
<div className="message-view-active-chat-container">
{messages.length > 0 &&
messages.map((message, i) => {
const lastMessageBool = messages.length - 1 === i + 1;
const userSentBool = userSent(currentUser, message);
const sameSenderAndNotCurrentUserBool =
sameSenderAndNotCurrentUser(i, messages, currentUser);
return (
<div
key={i}
ref={lastMessageBool ? setRef : null}
className={`message-view-message-container ${
userSentBool ? 'user-sent' : ''
}`}
>
<div
className="message-view-message-image-container"
style={
sameSenderAndNotCurrentUserBool || userSentBool
? { visibility: 'hidden' }
: { marginTop: '2px' }
}
>
<img
height="100%"
src={message.sender.picture}
alt="profile"
/>
</div>
<div className="message-view-text-container">
<div className="message-view-text">{message.text}</div>
<div
style={
sameSenderAndNotCurrentUserBool || userSentBool
? { display: 'none' }
: {}
}
className="message-view-text-info"
>
<p>
#{!userSentBool ? message.sender.userName : 'You'}
</p>
</div>
</div>
</div>
);
})}
</div>
{isTyping && (
<div className="lottie-container">
{typers.length ? getTyperString(typers) : ''}
<Lottie
animationData={animationData}
loop={true}
autoplay={true}
style={{ height: '16px', display: 'block' }}
/>
</div>
)}
<div
className="send-message-editable"
data-text={`Message `}
contentEditable
onKeyDown={handleKeyDown}
/>
</>
)}
</div>
);
};
export default MessageView;

Cannot delete multiple items on react using firestore

I am trying to delete multiple items on click of checkbox using firestore. But, onSnapshot method of firestore is causing issue with the state.
After running the code I can click on checkbox and delete the items, the items get deleted too but I get an error page, "TyperError: this.setState is not a function" in onCollectionUpdate method.
After refreshing the page I can see the items deleted.
Here's my code:
class App extends React.Component {
constructor(props) {
super(props);
this.ref = firebase.firestore().collection('laptops');
this.unsubscribe = null;
this.state = { laptops: [], checkedBoxes: [] };
this.toggleCheckbox = this.toggleCheckbox.bind(this);
this.deleteProducts = this.deleteProducts.bind(this);
}
toggleCheckbox = (e, laptop) => {
if (e.target.checked) {
let arr = this.state.checkedBoxes;
arr.push(laptop.key);
this.setState = { checkedBoxes: arr };
} else {
let items = this.state.checkedBoxes.splice(this.state.checkedBoxes.indexOf(laptop.key), 1);
this.setState = {
checkedBoxes: items
}
}
}
deleteProducts = () => {
const ids = this.state.checkedBoxes;
ids.forEach((id) => {
const delRef = firebase.firestore().collection('laptops').doc(id);
delRef.delete()
.then(() => { console.log("deleted a laptop") })
.catch(err => console.log("There is some error in updating!"));
})
}
onCollectionUpdate = (querySnapshot) => {
const laptops = [];
querySnapshot.forEach((doc) => {
const { name, price, specifications, image } = doc.data();
laptops.push({
key: doc.id,
name,
price,
specifications,
image
});
});
this.setState({ laptops });
console.log(laptops)
}
componentDidMount = () => {
this.unsubscribe = this.ref.onSnapshot(this.onCollectionUpdate);
}
getLaptops = () => {
const foundLaptops = this.state.laptops.map((laptop) => {
return (
<div key={laptop.key}>
<Container>
<Card>
<input type="checkbox" className="selectsingle" value="{laptop.key}" checked={this.state.checkedBoxes.find((p) => p.key === laptop.key)} onChange={(e) => this.toggleCheckbox(e, laptop)} />
...carddata
</Card>
</Container>
</div>
);
});
return foundLaptops;
}
render = () => {
return (
<div>
<button type="button" onClick={this.deleteProducts}>Delete Selected Product(s)</button>
<div className="row">
{this.getLaptops()}
</div>
</div>
);
}
}
export default App;
In the toggleCheckbox function you set the this.setState to a object.
You will need to replace that with this.setState({ checkedBoxes: items})
So you use the function instead of setting it to a object
You probably just forgot to bind the onCollectionUpdate so this referes not where you expectit to refer to.
Can you pls also change the this.setState bug you have there as #David mentioned also:
toggleCheckbox = (e, laptop) => {
if (e.target.checked) {
let arr = this.state.checkedBoxes;
arr.push(laptop.key);
this.setState({ checkedBoxes: arr });
} else {
let items = this.state.checkedBoxes.splice(this.state.checkedBoxes.indexOf(laptop.key), 1);
this.setState({
checkedBoxes: items
})
}
}
If you already did that pls update your question with the latest code.

useEffect() triggers components re-render in one function but not in the other one. Both function DO change state. What am I missing?

It must be something really silly I do wrong here. useEffect() works perfectly with MonthModificatorHandler but not re-render when using dayClick. When dayclick was only adding days re-render worked properly. After adding logic to remove days already in state re-rendering stopped. I can call saveChanges and loadTimeline to fix functionality but if you click few days in a row asynchronous call leads to unexpected results. Thanks for your time.
export default function DatePicker(props) {
const classes = useStyles();
const theme = useTheme();
const [monthModificator, setMonthModificator] = React.useState(0);
const [monthMatrix, setMonthMatrix] = React.useState([]);
const [selectedDates, setSelectedDates] = React.useState([]);
const MonthModificatorHandler = value => {
setMonthModificator(monthModificator + value);
};
const dayClick = day => {
let data = selectedDates;
let addDay = true;
if (data.length === 0) {
data.push(day);
} else {
data.map((date, index) => {
if (day.equals(date)) {
data.splice(index, 1);
addDay = false;
}
});
if (addDay) {
data.push(day);
}
}
setSelectedDates(data);
// saveChanges();
// loadTimeline();
};
let now = DateTime.local().plus({ months: monthModificator });
let firstDayOfFirstWeek = now.startOf("month").startOf("week");
let lastDayOfLasttWeek = now.endOf("month").endOf("week");
let monthToDisplay = Interval.fromDateTimes(
firstDayOfFirstWeek,
lastDayOfLasttWeek
);
function loadTimeline() {
axios.get(`/timeline`).then(response => {
let selectedDays = [];
response.data.map(date => {
selectedDays.push(DateTime.fromISO(date));
});
setSelectedDates(selectedDays);
});
}
useEffect(() => {
let load = true;
if (load) {
loadTimeline();
load = false;
}
var matrix = [];
for (let v = 0; v < monthToDisplay.length("day"); v++) {
matrix.push(firstDayOfFirstWeek.plus({ day: v }));
}
setMonthMatrix(matrix);
}, [selectedDates, monthModificator]);
function saveChanges() {
let arrayOfDataObjects = selectedDates;
let arrayOfDataStrings = arrayOfDataObjects.map(singleDataObject => {
return (
"," +
JSON.stringify(singleDataObject.toISODate()).replaceAll('"', "") // extra quotes removed
);
});
axios.post(`/timeline`, {
timeline: arrayOfDataStrings
});
}
return (
<Grid container justify="space-around">
<Button onClick={() => MonthModificatorHandler(1)}>+</Button>
<Button onClick={() => MonthModificatorHandler(-1)}>-</Button>
<Card className={classes.root}>
{monthMatrix.map((day, index) => {
let color = "secondary";
selectedDates.map(workingDay => {
if (day.equals(workingDay)) {
color = "primary";
}
});
return (
<Button
color={color}
variant="contained"
onClick={() => dayClick(day)}
className={classes.days}
key={index}
>
{day.day}
</Button>
);
})}
</Card>
<Button onClick={() => saveChanges()}>Save Changes</Button>
<Button onClick={() => loadTimeline()}>Update Changes</Button>
</Grid>
);
}
Maybe the problem is that you compute new state from previous state. It should be done with callback https://reactjs.org/docs/hooks-reference.html#functional-updates
Try something like
const dayClick = day => setSelectedDates((_data) => {
let data =[..._data];
let addDay = true;
if (data.length === 0) {
data.push(day);
} else {
data.map((date, index) => {
if (day.equals(date)) {
data.splice(index, 1);
addDay = false;
}
});
if (addDay) {
data.push(day);
}
}
return data
})
Answered by Kostya Tresko, thank you. On top of that, another mistake was in the hook itself. The way I loaded data caused re rending loop.
if (load) {
loadTimeline();
load = false;
}
DO NOT DO THAT

Cannot map through AsyncStorage data

I try to build react-native app with AsyncStorage with multiGet property. I can sucessfully add stuff to storage, and even display it in debugger console.log but when I want to map through array of data, it constantly shows no result. Does anyone knows where is the problem?
componentDidMount() {
this._getAllData();
}
_getAllData = async () => {
try {
const data = [];
const keys = await AsyncStorage.getAllKeys();
const items = await AsyncStorage.multiGet(keys, (err, stores) => {
stores.map((result, i, store) => {
// let key = store[i][0];
let value = store[i][1];
let parsedValue = JSON.parse(value);
data.push(parsedValue);
});
this.setState({ data });
});
} catch (error) {
this.setState({ error });
}
};
_displayAllData = () => {
// console.log(this.state.data.length);
// console.log(this.state.data);
this.state.data.length &&
this.state.data.map(el => {
return (
<View>
<Text>{el.name}</Text>
<Text>{el.street}</Text>
<Text>{el.postalCode}</Text>
<Text>{el.city}</Text>
<Text>{el.phone}</Text>
<Text>{el.email}</Text>
<Text>{el.nip}</Text>
</View>
);
});
};
and then while rendering my content i try
{this._displayAllData()}
Currently your _displayAllData method is not returning anything. You need to return the map result in order to have some JSX returned by your method.
_displayAllData = () => {
// console.log(this.state.data.length);
// console.log(this.state.data);
return this.state.data.length &&
this.state.data.map(el => {
return (
<View>
<Text>{el.name}</Text>
<Text>{el.street}</Text>
<Text>{el.postalCode}</Text>
<Text>{el.city}</Text>
<Text>{el.phone}</Text>
<Text>{el.email}</Text>
<Text>{el.nip}</Text>
</View>
);
});
};

When I use async in componentDidMount, Component will Mount and Unmount again and again. Why?

When I call getCityName component will unmount and DidMount again and again, unless I remove async .All the code is running in nextjs.
this.state = {
bank_account: {
// bank_name: '',
// number: '',
// city: '',
// branch_name: ''
},
allCity: []
};
componentDidMount() {
const { owner_cellphone } = this.props;
this.getDraft(owner_cellphone);
this.fetchCity();
}
fetchCity = async () => {
const { data, error } = await getCity();
if (error) {
return;
}
console.log(data);
this.setState({ allCity: data });
};
getCityName = cityString => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render() {
const { bank_account } = this.state;
const cityValue = this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
The reason it's not working because you are calling a async function from a sync function.
I am not sure it would work, but you can try..
getCityName = async (cityString) => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render = async () => {
const { bank_account } = this.state;
const cityValue = await this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
getCityName = cityString => {
const { allCity } = this.state;
if (allCity === [] || !cityString) {
return;
}
const cityArray = cityString.split(' ');
let targetProvince = allCity.find(item => item.code === cityArray[0]);
if (targetProvince) {
let newProvince = JSON.parse(JSON.stringify(targetProvince));
const targetCity = newProvince.children.find(item => item.code === cityArray[1]);
return `${targetProvince.name} ${targetCity.name}`;
}
return '';
};
I think it might be a problem of deep copy.

Resources