useSWR doesn't get data - swr

I have a useSWR call that calls a fetcher. The fetcher gets the data as shown in the log but useSWR doesn't error or get the data, it just stays in the loading state. I have other calls that use the same format without issue so I don't know what's going on.
The SWR...
export function GetPayoutHistory(
accessToken: string,
walletId: string,
page_limit: number,
page_number: number,
start_time: string,
end_time: string
): GetPayoutHistory {
const { data, error } = useSWR(
`${BASE_URL}/wallets/${walletId}/payouts?page_limit=${page_limit}&page_number=${page_number}&start_time=${start_time}&end_time=${end_time}`,
() =>
getPayoutHistory(
accessToken,
walletId,
page_limit,
page_number,
start_time,
end_time
),
{
onErrorRetry: onSWRRetry,
}
);
The fetcher...
export async function getPayoutHistory(
token: string,
walletId: string,
page_limit: number,
page_number: number,
start_time: string,
end_time: string
): Promise<PayoutHistory> {
return (
axios({
method: "get",
url: `${BASE_URL}/wallets/${walletId}/payouts?page_limit=${page_limit}&page_number=${page_number}&start_time=${start_time}&end_time=${end_time}`,
headers: {
Authorization: `Bearer ${token}`,
},
})
// .then((res) => res.data)
.then((res) => {
console.log("res.data", res.data);
return res.data;
})
);
}
In the component...
let now = new Date();
const { isPHErr, isPHLoading, pHError, pH } = GetPayoutHistory(
props.accessToken,
props.walletId,
100,
1,
"2022-01-01T00:00:00Z",
now.toISOString()
);
//...
if (isPHErr) {
return (
<>
<h1 style={{ margin: "10vh 0vh 10vh 0vh", textAlign: "center" }}>
Problem retrieving payout information.
</h1>
</>
);
}
if (isPHLoading) {
return (
<>
<h1 style={{ margin: "10vh 0vh 10vh 0vh", textAlign: "center" }}>
Loading...
</h1>
</>
);
}
if (pH) {
console.log("pH: ", pH);
}

Related

INVALID_STATE_ERR (React native)

What i am trying to do is simple. But not having enough knowledge about react state caused the problem. In my tab i took typing useState as false. I am trying to get the state when user is typing. By connecting websocket when user is typing i am sending type: "is_typing" data. And when receive is_typing i am trying to change the typing state to true and set timeout 500ms so that it won't show typing if it doesn't get onkeypress.
const Sock = () => {
const [typing, setTyping] = useState(false)
const url = endpoint + "chat/" + "thorOdin" + "/" + "?token=" + token
const ws = new WebSocket(url)
useEffect(() => {
if (token) {
ws.onopen = function () {
console.log("Chat Websoket Connected");
};
ws.onmessage = function (event) {
const data = JSON.parse(event.data);
if (data.command === "is_typing") {
setTyping(true)
setTimeout(() => {
setTyping(false)
}, 500);
}
};
ws.onerror = (e) => {
// an error occurred
console.log(e.message);
};
ws.onclose = function () {
console.log("WebSocket Client disconnected");
};
}
}, []);
function typingHandle(e){
ws.send(
JSON.stringify({
command: "is_typing",
text: `is typing ...`,
user: "me",
})
);
}
return (
<View>
{typing==true?
<Text style={{marginTop:50}}>Typing</Text>:
<View></View>
}
<TextInput placeholderTextColor={'gray'} style={{
color: 'white',
backgroundColor: '#262626',
borderRadius: 20,
paddingLeft: 10,
height: 40,
width: '80%',
marginLeft: 10,
marginTop:100
}} placeholder='Write Message..'
onKeyPress={typingHandle}/>
</View>
)
}
It's working. But after 4-5 sec it showing Uncaught error "INVALID_STATE_ERR". Can anyone tell what am i doing wrong here???
I am trying to get typing state of user through socket connection.

How to force update single component react native

I'm using 2 usestate in my component
const [choosedH, setChoosedH] = useState([]);
const [h, setH] = useState([]);
I have async method which fetch data from api and convert it to final array.
useEffect(() => {
getH();
}, [])
async function getH(){
const username = await SecureStore.getItemAsync('username')
const token = await SecureStore.getItemAsync('token')
axiosInstance.get('/api/xxx/' + username,
{
headers: {
Cookie: token,
},
},
{ withCredentials: true }
)
.then((response) => {
if(response.data.length > 0){
let singleH = {};
response.data.forEach(element => {
singleH = {
label: element.name,
value: element.name
}
h.push(singleH);
});
console.log(h)
}
})
.catch(function (error) {
console.log('There has been a problem with your fetch operation: ' + error.message);
throw error;
})
}
and finally i have my component:
<RNPickerSelect
onValueChange={(value) => setChoosedH(value)}
items={h}
useNativeAndroidPickerStyle={false}
style={{
...pickerSelectStyles,
iconContainer: {
top: 10,
right: 10,
},
}}
placeholder={{
label: 'Select',
value: null,
}}
Icon={() => {
return <Icon name="arrow-down" size={24} />;
}}
value={choosedH}
/>
I have a problem. After render my picker contain empty array. When render end, hook useEffect call getH() which give me data from api and convert it as I want to value of useState "h". How to force update picker items when function getH will end? It it possible to get data from api before render? Any ideas ?
I guess the problem is that you try to access h directly instead of using setH.
This should work:
if(response.data.length > 0){
const myArray = []
response.data.forEach(element => {
const singleH = {
label: element.name,
value: element.name
}
myArray.push(singleH);
});
setH(myArray)
console.log(h)
}

error when I want to display data from an API

In the screen that I'm trying to put up. I get the ID of a product. The previous screen being a list of products, which come from an API. I get this id thanks to props.data
The console.log ('props', this.props.data) works fine and returns the correct ID to me regardless of the product clicked.
With the ID of this product, I want to find the detailed information of this product (the reference, the family, the price, the stock etc.).
I created this function to call the product detail via my API.
initListData = async () => {
if (parseInt(this.state.productId) > 0) {
let product_data = await getProduct(this.state.productId);
console.log('product_data', product_data)
this.setState({
displayArray: product_data,
loadMoreVisible: (product_data.length >= 15 ? true : false),
currentPage: 2
});
}
};
I think the problem is that the displayArray[] is empty so
let product_data = await getProduct(this.state.productId);
Doesn't work.
And I get this error: Cannot update a component from inside the function body of a different component
Can you explain to me what's wrong ?
Full code
export default class Information extends Component {
constructor(props) {
super(props);
this.state = {
productId: this.props.data,
displayArray: [],
}
console.log('props', this.props.data) // ok, ça fonctionne, on récupère bien l'ID du produit cliqué
};
initListData = async () => {
if (parseInt(this.state.productId) > 0) {
let product_data = await getProduct(this.state.productId);
console.log('product_data', product_data)
this.setState({
displayArray: product_data,
loadMoreVisible: (product_data.length >= 15 ? true : false),
currentPage: 2
});
}
};
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
console.log('ça c\'est data = ', this.props.data );
console.log('ça c\'est les props =', this.props );
console.log('ça c\'est le state = ', this.state );
return (
<ScrollView contentContainerStyle={{flex: 1}}>
{
this.state.displayArray.map((item, i) => (
<ListItem bottomDivider>
<Icon name='flight-takeoff' />
<ListItem.Content>
<ListItem.Title style={{color: '#d35400'}}>{item.name}</ListItem.Title>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.family")}: {item.family_id}
</ListItem.Subtitle>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.reference")}: {item.reference}
</ListItem.Subtitle>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.id")}: {item.id}
</ListItem.Subtitle>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.cost")}: {item.cost}
</ListItem.Subtitle>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.description")}: {item.description}
</ListItem.Subtitle>
<ListItem.Subtitle style={{ color: '#F78400' }}>
{i18n.t("information.stock")}: {item.stock_status}
</ListItem.Subtitle>
</ListItem.Content>
</ListItem>
))
}
</ScrollView>
);
}
}
getProduct function : [I just have to hide the real url]
export async function getProduct(product_id) {
const abortController = new AbortController();
let user_id = await retrieveProfileUserId();
let lang = await retrieveAppLang();
let access_token = await renewAccessToken();
let result = {};
if (parseInt(product_id) > 0 && access_token != '' && parseInt(user_id) > 0) {
try {
let response = await fetch(
API_URL +
"/products/" + product_id +
"?user_id=" + user_id +
"&society_id=" + API_SOCIETYID +
"&access_token=" + access_token +
"&lang=" + lang,
{
method: "GET",
signal: abortController.signal,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + API_SECRETKEY,
"Cache-Control": "no-cache, no-store, must-revalidate",
Pragma: "no-cache",
Expires: "0"
}
}
)
.then(response => response.json())
.then(responseData => {
if (responseData.status == 200 && responseData.data) {
console.log("getProduct()::success", responseData.data);
result = responseData.data;
} else if (
responseData.status >= 200 && responseData.status <= 404 &&
responseData.data.error && responseData.data.error.length >= 3
) {
// Handle error
throw responseData.data.error;
} else {
throw "error";
}
});
} catch (error) {
//console.log(error);
abortController.abort();
}
}
Try changing a few things around and it should work.
I also don't like setting state directly inside the fn, so I propose the following change:
initListData = async () => {
if (this.props.data != null) {
const productData = await getProduct(this.state.productId);
return productData; // You are missing this, this is vital!
}
};
async componentDidMount() {
const data = await this.initListData(); // don't forget await here if you're using async
if (data.id) {
this.setState((prevState) => ({
displayArray: [...prevState.displayArray, data],
loadMoreVisible: ..., // I don't know what do you want here, because again, you receive an object from your backend, not an array.
currentPage: 2
}));
}
}

How to show an alert msg in react native when we have failed api response

I am not able to get an alert when the api response fails.I am trying to add the alert in where the actions are getting dispatched.Can we trigger an alert of we have redirect to a different page after the submission? Is there any other way to show the error msg when the api response fails.
const saveOPPInfo = () => {
this.setState({
_firstNameEmpty: isEmpty(_firstName),
_lastNameEmpty: isEmpty(_lastName),
_emailNotValid: !validateEmail(_email)
});
if (
this.isSaveAttempted ||
isEmpty(_firstName) ||
isEmpty(_lastName) ||
!validateEmail(_email)
) {
return;
}
this.isSaveAttempted = true;
this.setState({ _isFetching: true });
if (action === "ADD") {
const jsonPayload = [
{
email: _email,
firstName: _firstName,
lastName: _lastName,
isPrimary: false
}
];
addOtherPickupPerson({ purchaseContractId, jsonPayload }).then(() => {
GroceryNotifierApi.events().emitOnAction("saveOPPInfo", "");
});
} else {
const jsonPayload = {
email: _email,
firstName: _firstName,
lastName: _lastName,
isPrimary: false
};
editOtherPickupPerson({ purchaseContractId, jsonPayload, pickupPersonId: _id }).then(() => {
GroceryNotifierApi.events().emitOnAction("saveOPPInfo", "");
});
}
};
here is the part of jsx
<View
style={{
paddingLeft: 20,
paddingRight: 20,
paddingTop: 24
}}
>
{action === "ADD" ? (
<Body>
{OPP_ADD_OPP_DESC}
<Body
onPress={() => nativeNav.navigateToOPPLearnMore()}
style={{ textDecorationLine: "underline" }}
>
{OPP_LEARN_MORE}
</Body>
</Body>
) : (
<View />
)}
</View>
{_isFetching && (
<View style={{ height: 120 }}>
<CircularIndicator />
</View>
)}
<View style={styles.bottom}>
<PrimaryButton
style={styles.saveButton}
testID="save"
block
onPress={saveOPPInfo}
disabled={this.isSaveAttempted || _firstNameEmpty || _lastNameEmpty || _emailNotValid}
>
{isIOS ? "Save" : "SAVE"}
</PrimaryButton>
{action === "ADD" ? (
<View />
) : (
<LinkButton onPress={removeOPPInfo} disabled={this.isSaveAttempted}>
{OPP_REMOVE}
</LinkButton>
)}
</View>
</View>
);
}
this is how the action looks
export const addOtherPickupPersonRequest = createAction(ADD_OTHER_PICKUP_PERSON_REQUEST);
export const addOtherPickupPersonSuccess = createAction(ADD_OTHER_PICKUP_PERSON_SUCCESS);
export const addOtherPickupPersonFail = createAction(ADD_OTHER_PICKUP_PERSON_FAIL);
export const addOtherPickupPerson = ({ purchaseContractId, jsonPayload }) => {
return async function(dispatch) {
store.dispatch(addOtherPickupPersonRequest());
let response;
let responseJson;
try {
const startTime = new Date().getTime();
response = await fetchAddOtherPickupPerson(purchaseContractId, jsonPayload);
if (response.status >= 200 && response.status < 300) {
trackPerformance("checkout-add-opp-api", startTime, "performance-metric");
responseJson = await response.json();
store.dispatch(addOtherPickupPersonSuccess(responseJson));
return responseJson;
} else {
Alert.alert(
"Oops22!",
"Something is not working here. Please check back later.",
[{ text: "Okay" }],
{ cancelable: false }
);
await Promise.reject(err);
}
} catch (err) {
trackErrorEvent("checkout-add-opp-api", { ...defaultPayload({ err }) }, "api-error");
Alert.alert(
"Oops!",
"Something is not working here. Please check back later.",
[{ text: "Okay" }],
{ cancelable: false }
);
return store.dispatch(addOtherPickupPersonFail(err));
}
};
};
I want to show an alert when the response.status is not response.status >= 200 && response.status < 300)

How to navigate to another screen after axios action dispatch in reducer in react- native

I understand that this.props.isValidUser gets updated after action dispatches the axios promise. if the user is not valid is shows message. If the user is valid user, I want to navigate to another screen to enter pin. How do I navigate to another screen after I get axios result from action?
types.js
export const VALIDATE_USER = "VALIDATE_USER";
export const VALIDATE_PIN = "VALIDATE_PIN";
export const GET_ERRORS = "GET_ERRORS";
Reducer.js
import { VALIDATE_USER, VALIDATE_PIN, GET_ERRORS } from "../actions/types.js";
export default function (state = initialState, action) {
switch (action.type) {
case VALIDATE_USER:
return {
...state,
isValidUser: (action.payload == true) ? true : false,
Id: action.employeeId
};
case VALIDATE_PIN:
return {
...state,
isValidPin: action.payload,
action: "VALIDATE_PIN",
};
default:
return state;
}
}
action.js
import { GET_ERRORS, VALIDATE_USER, VALIDATE_PIN, } from "./types";
export const validateUser = (empId) => dispatch => {
axios.get(`${API}/api/Account/ValidateMobileAppUser?employeeId=${empId}`)
.then(res => {
dispatch({
type: VALIDATE_USER,
payload: res.data,
Id: empId,
});
})
.catch(err => {
dispatch({
type: VALIDATE_USER,
payload: false,
Id: empId
});
});
};
Login.js
import PropTypes from "prop-types";
import { validateUser } from "../actions/authActions";
class Login extends PureComponent {
constructor() {
super();
this.state = {
employeeId: "",
pin: "",
isValidUser: false,
};
this.onValidateUser = this.onValidateUser.bind(this);
this.onEmployeeId = this.onEmployeeId.bind(this);
}
onEmployeeId(employeeId) {
this.setState({ employeeId });
}
onValidateUser() {
this.props.validateUser(this.state.employeeId);
}
render() {
const { loading } = this.props.loading;
return (
<KeyboardAvoidingView style={styles.login} >
<ScrollView showsVerticalScrollIndicator={false}>
<Block padding={[10, theme.sizes.base * 2]} onPress={Keyboard.dismiss}>
<Block middle>
<Input
placeholder={this.state.placeholder}
keyboardType={this.state.keyboardType}
style={[styles.input]}
value={this.state.employeeId}
onChangeText={this.onEmployeeId}
/>
{(this.props.isValidUser == false) ? (
<Text center style={{ color: "#C00000", marginTop: 15, fontSize: 14 }}>
Employee Id not registered. Please contact HR.
</Text>
) : ""}
<Button
gradient
style={styles.loginButton}
onPress={this.onValidateUser}
>
<Text white center>
Login
</Text>
</Button>
</Block>
<Button
onPress={() => this.onGoToStep(1)}
style={{
borderWidth: 1,
borderRadius: 30,
borderColor: "#E46932"
}}
>
<Text gray caption center style={{ color: "#E46932" }}>
Don't have an account? Sign Up
</Text>
</Button>
</Block>
</ScrollView>
</KeyboardAvoidingView>
);
}
}
Login.propTypes = {
validateUser: PropTypes.func.isRequired,
errors: PropTypes.object.isRequired
};
function reducerCallback(state, ownProps) {
if (state.auth.isValidUser == true) {
ownProps.navigation.navigate("mPin", { Id: state.auth.employeeId, type: "LOGIN" });
}
}
const mapStateToProps = (state, ownProps) => ({
auth: reducerCallback(state, ownProps),
isValidUser: state.auth.isValidUser,
errors: state.errors
});
export default connect(
mapStateToProps,
{
validateUser,
}
)(Login);
this.props.isValidUser == false tells me if the user is valid or not. But if the user is valid I'm navigating to another screen using reducerCallback() function. I'm not aware if this is the correct way to do so. My question is how to I navigate to another screen after I get return result from async axios action and How to I set local state using setState when I get callback from axios dispatch. Please guide
Try to below code:
login.js:
onValidateUser() {
this.props.validateUser({
empId: this.state.employeeId,
onSuccess: () => {
//Navigate to other screen
},
onFailure: () => {
//Alert error message
},
});
}
Action.js:
export const validateUser = ({empId, onSuccess, onFailure}) => dispatch => {
axios
.get(
`${API}/api/Account/ValidateMobileAppUser?employeeId=${empId}`
)
.then(res => {
dispatch({
type: VALIDATE_USER,
payload: res.data,
Id: empId,
});
onSuccess();
})
.catch(err => {
dispatch({
type: VALIDATE_USER,
payload: false,
Id: empId
});
onFailure()
});
};