I'm trying to test this component using jest and enzyme in react native:
export class MyItem extends PureComponent {
_onDelete = () => {
console.log('Deleting !')
}
_renderSwpItem = () => {
return (
<TouchableOpacity id="delete" onPress={this._onDelete}>
<Icon name="delete" size={50} color={Colors.white}/>
</TouchableOpacity>
)
}
...
}
So far I wrote:
describe('Testing MyItem', () => {
const wrapper = shallow( <MyItem />)
it('_renderSwptItem press works', () => {
const instance = wrapper.instance()
const swpWrapper = shallow(instance._renderSwpItem())
const deletePressSpy = jest.spyOn(instance, '_onDelete')
swpWrapper.find('#delete').simulate('press')
expect(deletePressSpy).toHaveBeenCalledTimes(1)
})
})
I can see in the log Deleting ! but expect(deletePressSpy).toHaveBeenCalledTimes(1) fails. What am I doing wrong ?
It seems that calling spyOn before calling shallow(instance._renderSwpItem()) does the trick. Not sure why thought...
Related
I am new in react-native, in my application I am generating QRCode by one library and it working but in android it is taking time to show on UI, when I set that component to hook to show on UI then it stuck for while and every thing getting non-responsive. After some time it shows and everything work well.
So how can put that setWalletQR in background so that I can show loader until it show to UI?
Here is my code where I am generating the QR in InteractionManager to show
const PozReceive = ({ onClose }: ReceiveProps) => {
const [walletQR, setWalletQR] = useState<ConentQR>(null);
const generateWalletQrCode = () => {
const interactionPromise = InteractionManager.runAfterInteractions(() => {
const qrCode = ConentQR(user?.walletAddress || '', walletImg, 50);
setWalletQR(qrCode);
});
return () => interactionPromise.cancel();
};
useEffect(() => {
if (!pouchQR) {
generatePouchQrCode();
}
}, []);
return (
<Modal
coverScreen={true}
isVisible={true}
onBackdropPress={onClose}
onBackButtonPress={onClose}
backdropColor={Colors.DARK_PURPLE}
backdropOpacity={0.7}
style={styles.modal}>
<>
<BlurView
style={styles.blurView}
blurType="dark"
blurAmount={20}
reducedTransparencyFallbackColor="white"
/>
<VStack style={[styles.modalContainer]}>
{!walletQR ? (
<Image style={styles.qrLoader} source={loaderGif} />
) : (
walletQR
)}
</VStack>
</>
</Modal>
);
};
and here is QR code generator code :-
const ContentQR = (
content: string,
logo: Image.propTypes.source,
logoSize: number,
backgroundColor: string = 'transparent',
) => {
return (
<QRCode
color={Colors.DARK_PURPLE}
content={content}
codeStyle={'dot'}
outerEyeStyle={'diamond'}
logo={logo}
logoSize={logoSize}
backgroundColor={backgroundColor}
/>
);
};
Someone please help me I getting stuck here for while.
You can introduce a variable isLoading and render the loader based on this variable instead of qr value.
const PozReceive = ({ onClose }: ReceiveProps) => {
const [walletQR, setWalletQR] = useState<ConentQR>(null);
const [isLoading, setIsLoading] = useState<Boolean>(false);
const generateWalletQrCode = () => {
setIsLoading(true)
const interactionPromise = InteractionManager.runAfterInteractions(() => {
const qrCode = ConentQR(user?.walletAddress || '', walletImg, 50);
setWalletQR(qrCode);
setIsLoading(false)
});
return () => interactionPromise.cancel();
};
....
<VStack style={[styles.modalContainer]}>
{isLoading && <Image style={styles.qrLoader} source={loaderGif} />}
{!isLoaing && walletQR && walletQR}
</VStack>
export function Login() {
const [skip, setSkip] = useState(true);
const { data, isFetching } = useVerifyUserQuery(userState, {
skip,
});
const LoginButton = () => (
<Button
title="Login"
onPress={() => {
setSkip((prev) => !prev);
}}
/>
);
return (
…
)
}
The requirement is to make a request when the button is pressed, and then store the returned data in a constant. Is there a good way to make sure data is returned before I store it.
Here is one of my solutions. Obviously it may cause some problems.
onPress={() => {
setSkip((prev) => !prev);
while(isFetching){}
// save data
}}
And with the code below, storeData will be called multiple times.
export function Login() {
const [skip, setSkip] = useState(true);
const { data, isFetching } = useVerifyUserQuery(userState, {
skip,
});
if (!isFetching && IsNotEmpty(data)){
storeData();
}
const LoginButton = () => (
<Button
title="Login"
onPress={() => {
setSkip((prev) => !prev);
}}
/>
);
return (
…
)
}
It looks like you just want to use the lazy version - useLazyVerifyUserQuery instead of common. It will be like:
export function Login() {
const [ verifyUser ] = useLazyVerifyUserQuery();
const handleLogin = async () => {
const data = await verifyUser(userState).unwrap();
// Probably you would want to use `storeData` somehow here?
}
const LoginButton = () => (
<Button
title="Login"
onPress={handleLogin}
/>
);
return (
...
)
}
PS: just a warning - using a nested component definition, like LoginButton inside Login - is a known antipattern that may cause significant performance issues.
I want to sync the value of a useState in 2 different files from a useHook
I have a file named useChangeScreen witch I use to set when I want to show the diferent Views:
export const useChangeScreen = () => {
...
const [homeActivo, setHomeActivo] = useState(false);
const [searchActivo, setSearchActivo] = useState(true);
const [profileActivo, setProfileActivo] = useState(false);
...
const irAHome = () => {
setHomeActivo(true);
setSearchActivo(false);
setProfileActivo(false);
};
const irASearch = () => {
setHomeActivo(false);
setSearchActivo(true);
setProfileActivo(false);
};
const irAProfile = () => {
setHomeActivo(false);
setSearchActivo(false);
setProfileActivo(true);
};
...
return {
homeActivo,
searchActivo,
profileActivo,
irAHome,
irASearch,
irAProfile
}
}
This hook is called in the navigation component:
export const Nav = () => {
const {
irAHome,
irANotifi,
irAProfile,
irASearch
} = useChangeScreen();
...
return (
...
<TouchableOpacity onPress={irAHome}>
...
<TouchableOpacity onPress={irASearch}>
...
<TouchableOpacity onPress={irAProfile}>
...
)
}
and in the screen controller I have this:
export const ScreenController =() => {
const {
homeActivo,
searchActivo,
profileActivo,
} = useChangeScreen();
...
return(
...
{homeActivo ? (
<HomeScreen />
) : searchActivo ? (
<SearchShopsScreen />
) : profileActivo ? null : null}
...
)
}
when I press the buttons in the nav I want the views in ScreenController to change from Home to Profile or Search, but when I press the buttons, the state dont change
You can lift up the state to the parent component and pass it down to it's children, use React Context API or Redux.
If you chose to lift up the state:
Then you would have a parent component that looks like this:
// ...
const Parent = () => {
const {
irAHome,
irANotifi,
irAProfile,
irASearch,
homeActivo,
searchActivo,
profileActivo
} = useChangeScreen();
return (
<>
<Nav
irAHome={irAHome}
irANotifi={irANotifi}
irAProfile={irAProfile}
irASearch={irASearch}
/>
<ScreenController
homeActivo={homeActivo}
searchActivo={searchActivo}
profileActivo={profileActivo}
/>
</>
);
};
// ...
Then use the values passed from props like that:
export const ScreenController =({ homeActivo, searchActivo, profileActivo }) => {
// ...
return (
// ...
{homeActivo ? (
<HomeScreen />
) : searchActivo ? (
<SearchShopsScreen />
) : profileActivo ? null : null}
// ...
);
};
and:
export const Nav = ({
irAHome,
irANotifi,
irAProfile,
irASearch
}) => {
// ...
return (
// ...
<TouchableOpacity onPress={irAHome} />
// ...
<TouchableOpacity onPress={irASearch} />
// ...
<TouchableOpacity onPress={irAProfile} />
// ...
)
}
Note:
You should've actually used only one state which stores the current screen and checked for the current screen using comparison operators.
Checkout these for more details:
Lifting State Up
React Context API
Get Started with Redux
I have a function component UpdateCustomerScreen connect with redux store and use react-navigation navigate to SelectorGenderScreen.
selectedCustomer is my redux store data. I change the data on SelectorGenderScreen, when I use navigation.pop() to UpdateCustomerScreen. I have no idea how to re render the UpdateCustomerScreen.
Here is my UpdateCustomerScreen:
const UpdateCustomerScreen = ({ navigation, selectedCustomer }) => {
const gender = changeGenderOption(selectedCustomer.sex); // gender is an array.
const [sex, setSex] = useState(gender); // set array to my state.
console.log('sex', sex);
return (
<View>
<TouchableOpacity onPress={() => navigation.push('SelectorGenderScreen')}
<Text>Navigate to next screen</Text>
</TouchableOpacity>
</View>
);
const mapStateToProps = (state) => {
const { selectedCustomer } = state.CustomerRedux;
return { selectedCustomer };
};
export default connect(mapStateToProps, {})(UpdateCustomerScreen);
Here is my SelectorGenderScreen:
const SelectorGenderScreen = ({ navigation, selectedCustomer, changeGender }) => {
const gender = changeGenderOption(selectedCustomer.sex);
const [genderOption, setGenderOption] = useState(gender);
return (
<Header
title={Translate.chooseStore}
leftComponent={
<BackButton onPress={() => navigation.pop()} />
}
/>
<TouchableOpacity onPress={() => changeGender(selectedCustomer, genderOption)}>
<Text>Change the redux store data</Text>
</TouchableOpacity>
);
const mapStateToProps = (state) => {
const { selectedCustomer } = state.CustomerRedux;
return { selectedCustomer };
};
const mapDispatchToProps = dispatch => {
return {
changeGender: (selectedCustomer, genderOption) => {
dispatch(changeGender(selectedCustomer, genderOption));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SelectorGenderScreen);
I try to use useCallback() in UpdateCustomerScreen. When I navigation.pop(), It still doesn't re render.
// my state
const [sex, setSex] = useState(gender);
// It is not working
useCallback(() => {
console.log(sex);
},[sex]);
// It is not working
console.log('sex', sex);
return (
// my view
);
Any way to re render the UpdateCustomerScreen when redux store value has been changed ?
I'm new to react-native and formik and I encountered this problem that I'm trying to build up.
How can I fire a function in headerRight using Formik? I have updateCorporation function that will do fire api, and formik will do the job to fire this function and after I press the Update button, but the results are undefined
I didn`t understand why its happening.
File_1.js
const CorporationContainer = (props) => {
const {
navigation,
} = props;
const updateCorporation = (values) => {
// do patch stuff with values
// but its undefined
};
useEffect(() => {
navigation.setParams({ updateCorporation: updateCorporation.bind() });
}, []);
return (
<Corporation
updateCorporation={updateCorporation} />
);
};
CorporationContainer.navigationOptions = ({ navigation }) => ({
headerRight: (
<EditBtn
onPress={() => navigation.state.params.updateCorporation()}
>
<EditText>Update</EditText>
</EditBtn>
),
});
export default CorporationContainer;
File_2.js
const Corporation = (props) => {
const {
updateCorporation,
} = props;
const emailField = useRef(null);
const validationSchema = yup.object().shape({
email: yup.string()
.ensure()
.email('Email must be valid')
.required('Email'),
});
return (
<Formik
initialValues={{
email,
}}
onSubmit={values => updateCorporation(values)}
validateOnBlur={false}
validateOnChange={false}
validationSchema={validationSchema}
>
{(formProps) => {
const {
errors,
setFieldValue,
values,
} = formProps;
return (
<Container>
<Input
name="email"
placeholder="Email Corporation"
textContentType="emailAddress"
keyboardType="email-address"
returnKeyType="done"
autoCapitalize="none"
autoCorrect={false}
ref={emailField}
value={values.email}
onChangeText={setFieldValue}
editable={!email}
error={errors.email}}
/>
</Container>
);
}}
</Formik>
);
};
export default Corporation;
In File_1.js I had to use withForm and remove all Formik things in File_2.js and use the props instead.
const CorporationContainer = (props) => {
const {
navigation,
handleSubmit,
errors,
setFieldValue,
values,
} = props;
useEffect(() => {
navigation.setParams({ updateCorporation: handleSubmit.bind() });
}, []);
return (
<ProfileProfessional
errors={errors}
setFieldValue={setFieldValue}
values={values}
/>
);
};
CorporationContainer.navigationOptions = ({ navigation }) => ({
headerRight: (
<EditBtn
onPress={() => navigation.state.params.updateCorporation()}
>
<EditText>Editar</EditText>
</EditBtn>
),
});
export default withFormik({
// ...
})(CorporationContainer);
Formik author here...
Haven't tried this out, and idk exactly how navigation binding works, but you want to bind Formik's submitForm() prop to the navigation and not the updateCorporation function. However, You will need to do this where you have access to Formik props/context (i.e. as a child of <Formik>).
import React from 'react'
import { connect } from 'formik'
const updateCorporation = (values) => {
// do patch stuff with values
// but its undefined
};
const BindSubmit = connect(({ formik, navigation }) => {
useEffect(() => {
navigation.setParams({ updateCorporation: submitForm.bind() });
}, []);
return null
})
// ... and then just render it somewhere under Formik
const Corporation = () => {
return (
<Formik>
<BindSubmit />
{/* ... same */}
</Formik>
)
}