function with second argument react hooks - react-native

all i want is, after changing the state, i want to run the second argument..
without hooks.. this is what it looks like
state = {
gasTypeFrom: '',
}
setModal = item => {
setState({ gasType: item }, () => {
renderFrom();
});
};
this is what i tried with hooks
const [froms, setFroms] = useState({
gasType: 'Select Value Here',
displayModal: false,
});
function setModalFrom(item) {
useEffect(
() => {
setFroms({...froms, gasType: item});
},
() => {
renderModalFrom();
}
);
console.log('setModalFrom()', froms.gasType);
}
how do i do it in hooks with a second argument?

useEffect takes a function callback and a dependency array, so when a value in the dependency array is updated the effect is fired.
const [froms, setFroms] = useState({
gasType: 'Select Value Here',
displayModal: false,
});
useEffect(() => {
renderModalFrom();
}, [froms]); // when a value here updates, effect is run
...somewhere in your code
setFroms({...froms, gasType: item}); // this updates the value
useEffect documentation

Related

How to write a JEST test for a useEffect with timers?

I am using #react-native-community/netinfo to detect the app connectivity state and showing a message when connection is lost. How do I write a test for the following code that's in a useEffect to make sure that the message is showing/hiding and the cleanup works?
const { isConnected } = useContext(ConnectionContext);
...
useEffect(() => {
const snack = setTimeout(() => {
if (!isConnected) {
showMessage({
autoHide: false,
message: 'Please try again later',
});
}
}, 10000);
const hideSnack = setTimeout(() => {
if (isConnected) hideMessage();
}, 5000);
return () => {
clearTimeout(snack);
clearTimeout(hideSnack);
};
}, [isConnected]);
I have tried something like this to check if the app is connected
jest.mock('#react-native-community/netinfo', () => ({
...jest.requireActual('#react-native-community/netinfo'),
useNetInfo: () => ({
isConnected: true,
})
}));
You can use jest fake timers to control the time:
at the top use
jest.useFakeTimers();
then when you need to advance time by certain amount use :
jest.advanceTimersByTime(100);

State not updating from useEffect hook - Cannot read property 'title' of undefined

I am trying to update the state in my component using useEffect
useEffect(() => {
async function fetchData() {
let response = await getAccountCoverTypes();
setData(response);
}
fetchData();
}, []);
I am setting my default value:
const [data, setData] = useState({
payload: { title: "", accountCoverTypes: [] },
And trying to map through the data
const {
payload: { title, accountCoverTypes },
} = data;
{accountCoverTypes.map((button, index) => (
...
))}
When I try and use the data - I get the error, Cannot read property title of undefined. How can update the state from useEffect?
My service call:
const call = async () => {
try {
const response = await getItem(xxx);
return response
}
json returned:
{
"payload": {
"title": "text here",
"accountCoverTypes": [
{
"title": "sample text",
"details": "sample text",
"link": "test"
},..
]
}
}
Here you go with a solution
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
let response = await getAccountCoverTypes();
setData(response);
}
fetchData();
}, []);
{
data &&
data.accountCoverTypes &&
data.accountCoverTypes.length &&
data.accountCoverTypes.map((button, index) => (
...
))}
Before looping or iterating through the data variable, you should check the presence of data.
Ok, when your function first time executed data value will be undefined, as you do not set any initial value for that state const [data, setData] = useState()//initial value is not set;
Your approach is absolutely correct in sense of updating data using setData function invoked from your useEffect hook. Once your async function executed - data will be updated and component will be rerendered
React relies on the order in which Hooks are called.https://reactjs.org/docs/hooks-rules.html
That means that after the useEffect runs, the useState reinitializes the data
So move the useState before the useEffect in your code
const [data, setData] = useState();
useEffect(() => {
async function fetchData() {
let response = await getAccountCoverTypes();
setData(response);
}
fetchData();
}, []);

Reset useLazyQuery after called once

I'm using useLazyQuery to trigger a query on a button click. After the query is called once, the results (data, error, etc) are passed to the component render on each render. This is problematic for example when the user enters new text input to change what caused the error: the error message keeps reapearing. So I would like to "clear" the query (eg. when user types new data into TextInput) so the query results return to there inital state (everything undefined) and the error message goes away.
I can't find any clear way to do this in the Apollo docs, so how could I do that?
(I though of putting the query in the parent component so it does not update on each rerender, but I'd rather not do that)
This is how I have my component currently setup:
import { useLazyQuery } from 'react-apollo'
// ...
const [inputValue, setInputValue] = useState('')
const [getUserIdFromToken, { called, loading, data, error }] = useLazyQuery(deliveryTokenQuery, {
variables: {
id: inputValue.toUpperCase(),
},
})
useEffect(() => {
if (data && data.deliveryToken) {
onSuccess({
userId: data.deliveryToken.vytal_user_id,
token: inputValue,
})
}
}, [data, inputValue, onSuccess])
// this is called on button tap
const submitToken = async () => {
Keyboard.dismiss()
getUserIdFromToken()
}
// later in the render...
<TextInput
onChangeText={(val) => {
setInputValue(val)
if (called) {
// clean/reset query here? <----------------------
}
})
/>
Thanks #xadm for pointing out the solution: I had to give onCompleted and onError callbacks in useLazyQuery options, and pass the variables to the call function, not in useLazyQuery options. In the end the working code looks like this:
const [inputValue, setInputValue] = useState('')
const [codeError, setCodeError] = useState<string | undefined>()
const [getUserIdFromToken, { loading }] = useLazyQuery(deliveryTokenQuery, {
onCompleted: ({ deliveryToken }) => {
onSuccess({
userId: deliveryToken.vytal_user_id,
token: inputValue,
})
},
onError: (e) => {
if (e.graphQLErrors && e.graphQLErrors[0] === 'DELIVERY_TOKEN_NOT_FOUND') {
return setCodeError('DELIVERY_TOKEN_NOT_FOUND')
}
return setCodeError('UNKNOWN')
},
})
const submitToken = () => {
Keyboard.dismiss()
getUserIdFromToken({
variables: {
id: inputValue
},
})
}

Date Time Picker setState messing up my state

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 = () => {
};

Vue component doesn't update on route URL parameter change

So I have a component which executes code once it's mounted like this:
mounted(){
axios.get('/markers/' + this.username)
.then(response => {
this.markers = response.data.markers
}).catch((error) => console.log(error));
}
And I get the username like this:
username: this.$route.params.username
however, if I change the URL parameter, the username doesn't update so my AXIOS call doesn't update my markers. Why is this happening?
The reason is simple, even thought the URL is changing the component is not, VueJS is basically reusing the component and therefore not calling the mounted() method again.
Usually you can just setup a watcher and refactor a bit your code
methods: {
fetchData(userName) {
axios.get('/markers/' + this.username)
.then(response => {
this.markers = response.data.markers
}).catch((error) => console.log(error));
}
},
watch: {
'$route.params': {
handler(newValue) {
const { userName } = newValue
this.fetchData(userName)
},
immediate: true,
}
}
EDIT: Added the immediate true option and removed the mounted() hook