How can I access the state after setting it with React Hooks? - reactjs

I made 3 API calls in the same function. I call this function with the click method. But when I call the function, I cannot make the 2nd API call because the state is not updated. I can access the state outside the function but I need to access it inside. I've read some articles but still don't know exactly how to fix the problem.
Function:
const setInfos = async (e) => {
document.querySelector(".buyer-infos").classList.remove("dropdown-active");
await http
.get(`api/common/clients/getClients?kelime=${userValue}`, {
headers: {
Authorization: token,
},
})
.then((res) => {
if (res.status === 200 || res.statusText === "OK") {
// res.data.value.map((val) => console.log(val.Id));
// console.log("target =====", parseInt(e.target.id));
const companyId = parseInt(e.target.id);
res.data.value
.filter((fval) => fval.Id === companyId)
.map((val) => val.TCKN_VN !== null && setTckn(val.TCKN_VN));
console.log('2',tckn);
}
setUserValue(e.target.innerText);
})
.catch((err) => console.log(err));
await http
.get(`api/invoice/einvoice/checkCustomerTaxId/${tckn}`, {
headers: {
Authorization: token,
},
})
.then((res) => {
console.log("E-fatura kullanıcısı? =>", res);
setInvoiceUser(res.data.HttpStatusCode);
if (
res.data.value === "E-Fatura Kullanıcısı Değil" ||
res.data.message === "E-Fatura Kullanıcısı Değil"
) {
// res.data.value.map(val => console.log('http',val));
alert("E-Fatura Kullanıcısı Değil");
setTckn("");
document.querySelector(".tck-vergi-no").focus();
document.querySelector(".tck-vergi-no").style.border =
"1px solid #FF0000";
document.querySelector(".tck-vergi-no").style.outline = "none";
}
})
.catch((err) => console.log(err));
if (invoiceUser === 400) {
alert("E-Fatura Kullanıcısı Değil");
} else {
await http
.get(`api/common/clients/getClients?kelime=${userValue}`, {
headers: {
Authorization: token,
},
})
.then((res) => {
if (res.status === 200 || res.statusText === "OK") {
console.log("kelime", res.data.value);
res.data.value.forEach((val) => {
// console.log("INFOS", val.Id);
// console.log("infoos target", parseInt(e.target.id));
if (val.Id === parseInt(e.target.id)) {
// console.log('IF', val);
val.Title !== null ? setTitle(val.Title) : setTitle("");
val.TaxOffice !== null && setTaxOffice(val.TaxOffice || "");
val.Phone !== null && setTel(val.Phone || "");
val.Email !== null && setEmail(val.Email || "");
val.Fax !== null && setFax(val.Fax || "");
val.Country !== null && setCountry(val.Country || "");
val.City !== null && setCity(val.City || "");
val.Town !== null && setTown(val.Town || "");
val.Address !== null && setStreet(val.Address || "");
val.BuildingName !== null &&
setBuildingName(val.BuildingName || "");
val.BuildingNumber !== null &&
setBuildingNo(val.BuildingNumber || "");
val.DoorNumber !== null && setDoorNo(val.DoorNumber || "");
val.PostCode !== null && setPostCode(val.PostCode || "");
val.WebAdress !== null && setWeb(val.WebAdress || "");
}
});
setUserValue(e.target.innerText);
}
})
.catch((err) => console.log(err));
} };
State: const [tckn, setTckn] = useState("");

Related

How to make url api option short?

In this case I created a recursive promise function that is used to call different api urls. How do make the switch-case option in this code not too long?
let listUrl: Array<string> = ['URL-1', 'URL-2', 'URL-3', 'URL-n']
const rpcCall = async (baseURL: string) => {
switch (baseURL) {
case 'URL-1':
return new Promise((_resolve, reject) => fetch(baseURL)
.then(resolve => resolve.status !== 200 || 201 ? Promise.reject() : resolve)
.catch(reject)
);
case 'URL-2':
return new Promise((_resolve, reject) => fetch(baseURL)
.then(resolve => resolve.status !== 200 || 201 ? Promise.reject() : resolve)
.catch(reject)
);
case 'URL-3':
return new Promise((_resolve, reject) => fetch(baseURL)
.then(resolve => resolve.status !== 200 || 201 ? Promise.reject() : resolve)
.catch(reject)
);
case 'URL-n':
return new Promise((_resolve, reject) => fetch(baseURL)
.then(resolve => resolve.status !== 200 || 201 ? Promise.reject() : resolve)
.catch(reject)
);
default:
return Promise.reject();
}
}
const checkToken = async (index = 0): Promise<string> => {
new Promise((res, rej) =>
rpcCall(listUrl[index])
.then(res)
.catch((reject) => {
if (index < baseUrl.length && reject) {
return checkToken(index + 1)
.then(res)
.catch(rej);
} else {
rej();
}
})
);
return listUrl[index]
}
I mean the best practice method for the options
Your URLs cases are all the same, so they can be combined. You should also avoid the explicit Promise construction antipattern. 200 || 201 will not properly compare against both values either. The resolver function is also not the same as the Response object from fetch - better to name the parameter appropriately.
You don't need to construct another Promise in checkToken either - and that function also looks broken because it's creating a Promise that isn't being used anywhere. Another problem is that a Response object isn't a string; : Promise<string> is not an accurate typing. Perhaps you meant to use response.text() and something like
let listUrl: Array<string> = ['URL-1', 'URL-2', 'URL-3', 'URL-n']
const rpcCall = (baseURL: string) => {
const urls = ['URL-1', 'URL-2', 'URL-3', 'URL-n'];
if (!urls.includes(baseURL)) {
return Promise.reject();
}
return fetch(baseURL)
.then(response => {
if (response.status !== 200 && response.status !== 201) {
throw new Error();
}
return response.text();
});
};
const checkToken = async (index = 0): Promise<string> => {
return rpcCall(listUrl[index])
.catch((error) => {
if (index < listUrl.length && error) {
return checkToken(index + 1)
} else {
throw new Error(error);
}
});
};
Since this is TypeScript, you could also consider changing rpcCall to require that the string passed is one of the permitted URLs, thus forcing the caller to validate the potential argument first.
const urls = ['URL-1', 'URL-2', 'URL-3', 'URL-n'];
const rpcCall = (baseURL: typeof urls[number]) => {
return fetch(baseURL)
// etc

Websocket failed when first time rendering - React

new WebSocket() is failing when I firest render the application, but when I refresh the page it reconnects and works properly
error -> WebSocket connection to 'ws://localhost:3001/?editorId=a3tq1&testId=noTestIdGiven' failed:
Adding this for more information https://github.com/sanity-io/sanity/blob/next/packages/%40sanity/portable-text-editor/test/ws-server/index.ts
const webSocket = useMemo(() => {
console.log(editorId, incomingPatches$, testId)
const socket = new WebSocket(
`{ws://${window.location.hostname}:3001/?editorId=${editorId}&testId=${testId}`
);
socket.addEventListener('open', () => {
socket.send(JSON.stringify({type: 'hello', editorId, testId}))
})
socket.addEventListener('message', (message) => {
if (message.data && typeof message.data === 'string') {
const data = JSON.parse(message.data)
if (data.testId === testId) {
switch (data.type) {
case 'value':
setValue(data.value)
setRevId(data.revId)
onChange(data.value)
break
case 'selection':
if (data.editorId === editorId && data.testId === testId) {
setSelection(data.selection)
}
break
case 'mutation':
if (data.editorId !== editorId && data.testId === testId) {
data.patches.map((patch: Patch) => incomingPatches$.next(patch))
}
break
default:
// Nothing
}
}
}
})
return socket }, [editorId, incomingPatches$, testId])
const handleMutation = useCallback(
(patches: Patch[]) => {
console.log('webSocket >>', webSocket)
if (webSocket) {
webSocket.send(JSON.stringify({type: 'mutation', patches, editorId, testId}))
}
},
[editorId, testId, webSocket] )

Can't find variable error in `getDerivedStateFromProps`

I am updating some deprecated code in a react native app, and so I replaced this:
UNSAFE_componentWillReceiveProps(nextProps) {
if(
this.props.started != nextProps.started ||
this.props.stopped != nextProps.stopped ||
this.props.durationOverride != nextProps.durationOverride ||
this.props.editable != nextProps.editable
) {
this.loadTimer(nextProps);
}
}
... with this:
static async getDerivedStateFromProps(nextProps, props) {
if(
props.started != nextProps.started ||
props.stopped != nextProps.stopped ||
props.durationOverride != nextProps.durationOverride ||
props.editable != nextProps.editable
) {
await loadTimer(nextProps);
}
return null;
}
This works as expected, functionally. However, I am getting this yellow text message in the console:
[Unhandled promise rejection: ReferenceError: Can't find variable:
loadTimer]
What is the issue here? Is it that I'm calling a function within getDerivedStateFromProps()? Or is the issue that it's a promise? Or is the issue to do with the loadTimer() function itself? That function looks like this:
async loadTimer(props) {
await Promise.all([
new Promise((resolve) => {
if(props.editable !== undefined) this.setState({
editable: props.editable,
}, resolve);
}),
new Promise((resolve) => {
if(props.stopped) {
this.setState({
stopTime: moment(props.stopped).format('YYYY-MM-DD HH:mm:ss'),
stopDuration: moment(props.stopped).format('HH:mm'),
}, resolve);
} else {
this.setState({
stopTime: null,
stopDuration: null,
}, resolve);
}
}),
new Promise((resolve) => {
if(props.stopTimeOffset && props.stopTimeOffset != Number.NaN) {
this.setState({ stopTimeOffset: props.stopTimeOffset }, resolve);
} else {
this.setState({ stopTimeOffset: 0 }, resolve);
}
}),
new Promise((resolve) => {
if(props.started) {
this.setState({
startTime: moment(props.started).format('YYYY-MM-DD HH:mm:ss'),
startDuration: moment(props.started).format('HH:mm'),
}, resolve);
} else {
this.setState({
startTime: null,
startDuration: null,
}, resolve);
}
})
]);
if(!props.stopped && props.started && this.state.editable) {
await this._startTimer();
} else {
if(props.durationOverride && !this.state.editable) {
this.setState({
duration: `${props.durationOverride}:00`,
durationShort: props.durationOverride,
});
} else {
const duration = this._getDuration();
this.setState({
duration: duration.long,
shortDuration: duration.short,
});
}
}
}
This isn't the kind of case that getDerivedStateFromProps is designed for. I recommend you put your code in componentDidUpdate instead:
async componentDidUpdate(prevProps) {
if(
this.props.started != prevProps.started ||
this.props.stopped != prevProps.stopped ||
this.props.durationOverride != prevProps.durationOverride ||
this.props.editable != prevProps.editable
) {
await this.loadTimer(this.props);
}
}
You may find the following article from the react team useful: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

What is wrong with this awaitReaction?

I was just creating a command, but then I got this error:
(node:16632) UnhandledPromiseRejectionWarning: DiscordAPIError: Cannot send messages to this user
Here is my code:
.then(function (message) {
try {
message.react("🟩")
message.react("🟨")
message.react("🟥")
message.react("⬛")
} catch (error) {
console.error('One of the emojis failed to react.');
}
})
.then(async m => {
await message.react('🟩' || '🟨' || '🟥' || '⬛')
var filter = (u, r) => {
u.id == msg.author.id && r.emoji.name == '🟩' || r.emoji.name == '🟨' || r.emoji.name == '🟥' || r.emoji.name == '⬛'
}
message.awaitReactions(filter, { time: 30000 })
.then(collected => {

React Context API; Is it possible to access updated context from a sibling hook

A button calls the function signAllBrowsed, which contains two other functions:
loadSafetyLetters is a hook that makes a database call for some data and sets it in context
signAll is a hook that tries to access data in context to do something with it
Context is getting set properly, but when signAll accesses it, the data is not updated. Is there a way to access the updated context without directly passing it to the 2nd function? Or is there a way to call a callback once context is updated and accessible? Seems the updated context is only available after a re-render.
The component containing signAllBrowsed and the 2 hooks are siblings.
code in above image:
setModalVisible(true)
const logHeader = 'SafetyLetterHome::SignAllBrowsed'
try {
const response = await loadSafetyLetters(false) // before beginning sign all, get a fresh list of letters from db
if (Configs.SHOW_REQUEST_LOGS) console.log(`${logHeader} response`, response)
if (response === 'no api error') {
await signAll()
navigation.navigate('SafetyLetterSign')
}
} catch (error) {
const errorMessage = error.status && error.status.message ? error.status.message : error
Alert.alert('Database Error', errorMessage)
console.log(`${logHeader}`, errorMessage)
}
}
loadSafetyLetters calls the loadLetters hook:
const [getLetters] = useGetLetters()
const [sortLetters] = useSortLetters()
const [hasAPIError] = useHasAPIError()
const navigation = useNavigation()
const { setModalVisible, setShowSignAll, setSortedLetters, setUnsortedLetters } = useContext(SafetyContext)
const loadLetters = async (sort = true) => {
try {
const response = await getLetters()
const logHeader = 'SafetyHome::loadLetters'
const errorMessage = 'The following error occurred when trying to load letters:'
if (Configs.SHOW_REQUEST_LOGS) console.log(`${logHeader} response`, response)
const error = hasAPIError(response, logHeader, errorMessage)
if (error) return error
const { data } = response.data.payload
let unsortedLetters = []
if (data !== null && data.length > 0) {
data.map((item) => {
// grab only unsigned letters
if (
item.assignmentStatus === SafetySources.PENDING ||
item.assignmentStatus === SafetySources.BROWSED ||
item.assignmentStatus === SafetySources.QUESTIONS_COMPLETE
) {
unsortedLetters.push({
safetyLetterId: item.safetyLetterId,
title: item.title,
assignmentStatus: item.assignmentStatus,
filePath: item.filePath,
embeddableToken: item.embeddableToken,
sponsorId: item.sponsorId,
letterDate: item.letterDate,
form16: item.form16Enabled === '1' ? true : false,
sponsorName: item.sponsorName,
type: item.letterType,
sortOrder: item.sortOrder, // dear doctor; sortOrder === 1
})
}
})
}
if (unsortedLetters.length > 0) {
let bletters = unsortedLetters.filter((letter) => letter.assignmentStatus === SafetySources.BROWSED || letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE)
console.log('useLoadLetters; setting fresh pull of letters in context, including ', bletters.length, ' browsed letters')
setUnsortedLetters(unsortedLetters) // set in context
setShowSignAll( // show/hide sign all button
unsortedLetters.some((letter) =>
letter.assignmentStatus === SafetySources.BROWSED ||
letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE,
))
}
if (sort) {
if (unsortedLetters.length > 0) {
let sortedLetters = sortLetters(unsortedLetters) // sort letters with hook
setSortedLetters(sortedLetters) // set in context
}
}
} catch (error) {
console.log('SafetyHome::loadLetters ', error)
const errorMessage = error.status && error.status.message ? error.status.message : error
Alert.alert(
'Error Loading Letters',
`A database error has occurred. Please try again. (${errorMessage})`,
)
navigation.navigate('Home')
} finally {
setModalVisible(false)
}
}
return [loadLetters]
}
signAll hook:
const { state: { unsortedLetters },
setF16Browsed,
setQcAndBrowsed,
setModalVisible,
setSelectedLetter
} = useContext(SafetyContext)
const signAll = async () => {
let qcAndBrowsed = [] // set letter groups in context
let f16Browsed = []
unsortedLetters.forEach((letter) => {
if (
letter.assignmentStatus === SafetySources.BROWSED ||
letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE
) {
if (
letter.form16 &&
letter.assignmentStatus !== SafetySources.QUESTIONS_COMPLETE
) {
f16Browsed.push(letter)
} else {
qcAndBrowsed.push(letter)
}
}
})
setQcAndBrowsed(qcAndBrowsed)
setF16Browsed(f16Browsed)
// begin sign all with first f16 letter
if (f16Browsed.length > 0) {
setSelectedLetter(f16Browsed[0])
} else {
setSelectedLetter(null) // clear any previous viewed letter
}
setModalVisible(false)
}
return [signAll]
}

Resources