how to conditionally call an api depending on received value - javascript

i need to call an api depending on the value that im receiving on state,
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
const [selectedValue, setSelectedValue] = useState([])
const firstAPI = async (val) => {
return await axios
.get(`http://localhost:3000/api/v1/rendition?name=${val}&&excel=true`)
.then(res => console.log('excel', res))
}
const secondAPI = async (val) => {
const limit = pageSize * (page - 1);
return await axios
.get(`http://localhost:3000/api/v1/rendition?id=${val}&&skip=${limit}&&take=${pageSize}&&excel=true`)
.then((res) => {
console.log('excelID', res);
})
}
const handleExcelClick = (param) => {
if(param.filter(item => typeof item === 'string')){
firstAPI(param)
} else {
secondAPI(param)
}
}
<Button onClick={() => handleExcelClick(selectedValue)} > Excel </Button>
so im receveing a value from 2 different inputs, which are stored in selectedValue state, which can either be a string or a number, so i need to call an api depending on the value, if its a string, call the firstAPI, if it's a number, call the secondAPI. But whatever condition i put in, it ignores the if statement and just calls the first api which also triggers a 500 error because one of the apis only takes numbers as params, i don't know other way to approach this, any help will be much appreciated

Try with:
...
if(param.filter(item => isNaN(item) )){
firstAPI(param)
} else {
secondAPI(param)
}
...

So i solved it a lot of days ago, but i forgot to post my solution, so here it goes:
const handleExcelClick = (param: any) => {
if (typeof param === 'number') {
return firstAPI(param);
} else if (param.some((i: any) => typeof i === 'string')) {
return secondAPI(param);
}
};
i basically forgot to add a return to each statement, and there was also no point in filtering values based on just a filter method, so i used the "some" array method to just receive the values that return true if the condition is met, which is checking if the data type matches the statement
hope this helps anyone trying to do the same

Related

Rendering a promise return value with React.useEffect

I'm trying to render the value returned from a promise that calls two external APIs into the JSX.
It seems to work the first time, but then I get undefined.
Am I using useEffect the wrong way?
export default function Dashboard(props) {
const [pageInfo, setPageInfo] = React.useState();
async function getPageInfo(props) {
try {
const userId = localStorage.getItem('userId');
let res = await axios.get('http://localhost:8000/user/'+userId);
let lastPageId = res.data.pages[res.data.pages.length - 1];
let pageInfoObj = await axios.get('http://localhost:8000/page/'+lastPageId);
return pageInfoObj.data;
} catch (err) {
console.log(err);
}
};
React.useEffect(() => {
getPageInfo(props)
.then(data => {
setPageInfo(data);
})
}, [props]);
return(
<Typography>
{pageInfo.pageTitle}
</Typography>
);
}
use optional chaining or initialize your data as per the response structure and put conditionals
If response is an array, initialize with an empty array (so you can map over and get no error's) and also you can check it's length before rendering like data.length > 0 && data.map()
If response is an object, initialize with null so you can check as data && data.blah
If it's a string then may be an empty string 😛
depends on response structure and it's not mandatory to have exactly same
why ? because, by the first render still the data i.e., pageInfo is not available yet and it will be undefined as it is not initialized with any value so by default is undefined
return(
<Typography>
{pageInfo?.pageTitle}
</Typography>
);
Looks like there is a backend fetch error in the second time and also initialise your state.
In your code, when there is an error in the fetch call, it simply returns undefined; So, handle the error in the .catch chain.
export default function Dashboard(props) {
const [pageInfo, setPageInfo] = React.useState({});
async function getPageInfo(props) {
const userId = localStorage.getItem('userId');
let res = await axios.get('http://localhost:8000/user/' + userId);
let lastPageId = res.data.pages[res.data.pages.length - 1];
let pageInfoObj = await axios.get('http://localhost:8000/page/' + lastPageId);
return pageInfoObj.data;
};
React.useEffect(() => {
getPageInfo(props)
.then(data => {
setPageInfo(data);
}).catch(err => {
console.log("Some Error: " + err.message)
})
}, [props]);
return (
<Typography>
{pageInfo.pageTitle}
</Typography>
);
}

How to fetch up-to-date data inside async?

I have a function which fetches data.
const fetchData = async (filter) => {
if (loading) return
loading = true
const data = await api(filter)
setData(data)
loading = false
}
I also have a filter component, when I change filters it calls my fetchData() function with the new filter variable.
This all works, however there is an issue
This issue occurs when I switch my filter but my fetching function is in a loading state. This causes the if check to fail and I now see outdated data because a re-fetch never happens.
My initial idea was to create a const q = [] variable, and inside if (loading) i would push my filters, and somehow at the end I would re-fetch with the last element inside my q array and then clear that array.
I dont really know how to do that re-fetch logic. A setInterval(() => checkQ(), 1000)? Doesn't seem right
What would be a better approach to take?
You should use an AbortController - that's part of the fetch, as my experience tells me that it's not hard to initiate a new fetch request, but what to do with the first request, when you send out a second?
Here's a snippet that will do the thing you asked - but also deals with the unnecessary requests:
const { useState, useEffect } = React
const useFetchData = () => {
const [users, setUsers] = useState([])
let controller = null
const fetchData = () => {
console.log('fetch initiated')
if (controller) controller.abort()
controller = new AbortController();
const { signal } = controller;
fetch('https://jsonplaceholder.typicode.com/users', { signal })
.then(response => {
console.log('request response')
return response.json()
})
.then(json => {
console.log('retrieved list:', json)
setUsers(() => json || [])
})
.catch(err => {
if(err.name === "AbortError") {
console.warn('Abort error', err)
}
})
}
return { fetchData }
}
const FetchData = () => {
const { fetchData } = useFetchData()
return (
<div>
<button onClick={fetchData}>FETCH DATA</button><br />
</div>
)
}
const FetchAbortFetchData = () => {
const { fetchData } = useFetchData()
return (
<div>
<button onClick={() => {
fetchData()
fetchData()
}}>FETCH-ABORT-FETCH DATA</button><br />
</div>
)
}
const App = () => {
return (
<div>
<FetchData /><br />
<FetchAbortFetchData />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Easiest way is to use your filter criteria as the lock.
Advantages
Always fetching data immediately
Only calling setData with the results from the most recent filter criteria
Simple
Disadvantages
May have multiple simultaneous requests
Toggling back/forth between filters may lead to a race condition.
let latestFilter = null;
const fetchData = async (filter) => {
// Check to see if its there's the same request already in progress
// Note: may need to deep equal to check the filter
if (filter === latestFilter) return;
// Set the current request as most up to date
latestFilter = filter
// Fetch Data (async)
const data = await api(filter)
// If the filter is still the most up to date, use it. Otherwise discard.
// Note: may need to deep equal to check the filter
if (filter === latestFilter) {
setData(data)
latestFilter = null
}
}
In order to solve disadvantage 2, you can include a counter. This will ensure that only the most recent request will run setData.
let latestFilter = null;
let latestRequestNumber = 0
const fetchData = async (filter) => {
if (filter === latestFilter) return;
latestFilter = filter
// Identify the current request
const requestNumber = latestRequestNumber + 1;
// Store this number
latestRequestNumber = requestNumber
const data = await api(filter)
// Update data only if we are the most recent request.
if (callCount = latestCallCount) {
setData(data)
latestFilter = null
}
}

Filter through an array and return Params

I am trying to filter through an array of data from my API, however, the data returns unfiltered, I am thinking its something to do with the way I structured my method or It could be that my params are not being returned. I have below both my codes. I would really appreciate any effort. Thanks
Trying to filter data
const [catalogueArray, setCatalogueArray] = useState([])
useEffect(() => {
catalogueList()
}, [])
const catalogueList = () => {
const catalogue = data.filter(item => {
item.storeName == navigation.state.params.enteredStore
return { ...item }
})
setCatalogueArray(catalogue)
}
Setting Params
const handleSelectedStore = (name) => {
setSelectStore({ selectStore: name })
navigation.navigate('StoreCatalogue', { selectStore: name })
}
You are returning an object, not a boolean in filter(). Objects are truthy so you are effectively doing no filtering at all and are getting the same result as if you did return true
The == comparison above the return is doing nothing since you aren't using the result of that comparison anywhere
The equality check is what you want to return. If you want new objects in the array you need a map() for that in another step
const catalogue = data.filter(item => {
return item.storeName == navigation.state.params.enteredStore
}).map(item => ({ ...item }));

Redux observable epic takes few actions in a row but return only last

I have an epic which should proceed few actions of type VERIFY_INSURANCE_REQUEST in a row. Everything works good inside switchMap block (all items are proceeded as well) but only last one goes to map block, so I have only one successfully dispatched action instead of many.
function verifyInsuranceEpic(action$) {
return action$.pipe(
ofType(types.VERIFY_INSURANCE_REQUEST),
switchMap((action) => {
const { verifyInsuranceModel } = action;
const promise = InsuranceApi.verifyInsurance(verifyInsuranceModel).then(result => {
const returnResult = result && result.rejectReason === null;
const actionResponse = {
returnResult,
key: verifyInsuranceModel.key
}
return actionResponse;
})
return from(promise);
}),
map(result => {
return verifyInsuranceSuccess(result)
}),
catchError(error => of(verifyInsuranceFailure(error)))
);
}
Is there any way to make all responses go to map block?
As mentioned in comments, the solution is just change switchMap to concatMap.

Redux Thunk handling network responses in a different order

I have a text entry field which (via a Thunk) queries the server of the validity of the user's text. As these requests can happen in very quick succession, they may be returned from the server in a different order than when they were sent. Therefor, the string in the text field may be shown as invalid, when in fact it is valid.
To fix this, I'm performing a check when I receive a response from the server - is the current content of the text field the same as what was checked? If not, check again. I feel there should be a better way to handle this situation than querying a DOM element value.
How can I follow through from pre to post server request?
export function updateUserCode(code) {
return dispatch => {
return dispatch(validateUserCode(code))
}
}
function validateUserCode(code) {
return dispatch => {
dispatch(updateCode(code))
return fetch(`/api/code/${code}`)
.then(response => response.json())
.then(json => dispatch(receiveValidatedCode(code, json)))
.catch(error => {Log.error(error)})
}
}
function receiveValidatedCode(code, data) {
const lastval = document.getElementById('usercode').value
if (code != lastval) {
// Code not the same as current value
// need to validate again
return updateUserCode(lastval)
}
if(data.success) {
return {
type: types.CODE_VALIDATED,
code: code,
}
}
return {
type: types.CODE_INVALID,
reason: data.reason,
}
}
Messing with DOM inside your logic is indeed less than ideal. I would suggest keeping last entered text field value in Redux store and perform your checks in reducer.
Also I don't see any point in re-validating user input if current entered value differs from one validated by last resolved request. Just ignore such responses and do not perform unnecessary request.
In terms of code you can do it like that:
// actions
const requestValidation = value => ({ type: 'request-validation', value });
const receiveValidation = (value, result) => ({ type: 'receive-validation', value, result });
export const validateUserCode = code => dispatch => {
dispatch(requestValidation(code));
return fetch(`/api/code/${code}`)
.then(response => response.json())
.then(json => dispatch(receiveValidation(code, json)))
.catch(error => {Log.error(error)})
}
// reducer
export const validationReducer = (state = {}, action) => {
if (action.type === 'request-validation')
return ({ value: action.value, isValidating: true });
if (action.type === 'receive-validation' && state.value === action.value)
return ({ value: action.value, isValid: !!action.success });
return state;
};
That is not production quality code, and I don't event sure if it works, but it reflects the idea.

Categories

Resources