I'm fetching a list of categories via the useEffect Hook:
useEffect(() => {
fetch(
`/read.php?catId=${catId}`
)
.then(res => res.json())
.then(res => {
setProducts([...products, ...res.data]);
});
}, []);
and store the data in a useState Hook
const [products, setProducts] = useState([]);
The {catId} I'm using in the URL is forwarded via routeName from the previous screen and I get it with getParam like that:
const catId = props.navigation.getParam("categoryId");
That's working great for inside of the functional component. The problem is, I also need props of the 'products' variable in my StackNavigator. Therefore I tried to use navigationData function of navigationOptions, once again getParam and use the find method on the products variable:
CategoryProductsScreen.navigationOptions = navigationData => {
const catId = navigationData.navigation.getParam("categoryId");
const selectedCategory = products.find(cat => cat.id === catId);
return {
headerTitle: selectedCategory.category_name
};
};
Unfortunately the 'products' variable is not available outside of the functional component. I can't also use useEffect outside to fetch my data again, obviously.
Is there a way to get data from my useState variable in StackNavigator?
Thank you
Related
I have a flatlist and inside the onViewableItemsChangedRef I want to set the value of another ref inside the component like so:
const MyComponent= (): JSX.Element => {
const currentId = useRef('');
const onViewableItemsChangedRef = useRef(({ viewableItems }) => {
// I want to set currentId in here
});
return (
<FlatList
// other props here like data, renderItem, etc.
ref={ref}
keyExtractor={(item) => item.id
onViewableItemsChanged={onViewableItemsChangedRef.current}
/>
);
};
But when I try to set currentId.current inside of onViewableItemsChangedRef, it's always undefined.
I tried switching the useRef to useCallback instead, but I would keep getting the error listed here React Native FlatList onViewableItemsChanged callback encounter error after state changed rerender
Is there another way of doing this?
onViewableItemsChangedRef.current contains the first function definition which is bound to the first values, instead you can use useCallback calling a ref function like this:
const onViewableItemsChangedRef = useRef()
onViewableItemsChangedRef.current = ({ viewableItems }) => {
// I want to set currentId in here
});
onViewableItemsChanged = useCallback((...args)=>onViewableItemsChangedRef.current(...args),[])
// now use onViewableItemsChanged instead of onViewableItemsChangedRef.current
Of course, you can use the effective arguments instead of ...args
I want to useQuery renders whenever the state changes
is there any option in useQuery hook
`export const Search = ({ navigation }) => {
const [search, setSearch] = useState();
const [dismiss, setDismiss] = useState(false);
const [searchResult, setSearchResult] = useState();
const searchHander = (query) => {
setSearch(query)
setDismiss(true)
}
const searching = useQuery(['searching', search], () => api.search(search));
useMemo(() => {
setSearchResult(searching?.data ? searching?.data?.results : []);
}, [searching?.data])
const searchResults = ({ item }) => {
return <View style={{ marginVertical: 10 }}><SearchResults navigation={navigation} data={item} /></View>
}
const desmiss = useRef(null);
return (...)}`
useQuery is not depend to state
I don't fully understand the question:
I want to useQuery renders whenever the state changes
I'm just assuming you want to refetch when the state changes, because when state changes, your component does render.
For this, all you need to do is add search to the queryKey, which you have already done.
Further, I can see that you are calling setSearchResults in useMemo which is a) unnecessary because react-query already gives you the result and b) violates component purity because you call setState during render.
I think the component should just be:
const [search, setSearch] = useState();
const [dismiss, setDismiss] = useState(false);
const [searchResult, setSearchResult] = useState();
const searchHander = (query) => {
setSearch(query)
setDismiss(true)
}
const searching = useQuery(['searching', search], () => api.search(search));
const searchResult = searching?.data?.results ?? []
Then you can work with searchResult, and whenever setSearch is called, your query will refetch.
im a new to react native but trying to build my own application.
I'm trying to pass storeKey and userName obtained from DB to CustomDrawer and Drawer.Screen so I don't need to repeat the function everytime.
so, it's working inside HomeScreen, console.log prints proper value. However, when I pass it in Drawer.Screen 'stock', and then print it in that module, it shows empty array.
How can I pass value from async to drawer navigator properly?
and how can I pass it to CustomDrawer? will {...props} already contain the value?
When I print props.params in the CustomDrawer module, it only says undefined..
const HomeScreen = ({ navigation }) => {
const [storeKey, setStoreKey] = useState([]);
const [userName, setName] = useState([]);
useEffect(async() => {
let isMounted = true;
if(isMounted){
console.log('zzzz')
const auth = getAuth();
const user = auth.currentUser;
if(user !== null){
const email = user.email;
const UserInfo = await getDoc(doc(db, 'users', email));
if(UserInfo.exists()){
setName(UserInfo.data().name);
setStoreKey(UserInfo.data().storeKey)
return () => {
isMounted = false
}
}
else{
console.log('None')
}
}
}
}, [storeKey]);
console.log('this',storeKey)
return (
<Drawer.Navigator drawerContent={props => <CustomDrawer {...props} />} screenOptions={headerStyles} initialRouteName={HomeScreen} >
<Drawer.Screen name='Search' component={SearchScreen} options={QuitIcon}/>
<Drawer.Screen name='Stock' component={StockScreen} options={QuitIcon} initialParams={{storeKey: storeKey}}/>
</Drawer.Navigator>
)
}
Even if the UseEffect is async or none the following code will be the same,
when calling the console.log('this', storeKey) the data is not yet in the state, to wait the useEffect run before continue the code, you have to add an empty array as a second argument in the useEffect function like this :
useEffect(() => {
}, []) // <-- empty array here
by this way your useEffect will be run only the first render of the app and the program will wait the useEffect to be run before continue.
I'm developing a react native mobile app using UI Kitten. I'm still fairly new to both react native and UI kitten, so I am using the just the plain Javascript template VS the Typescript template.
I have a functional component screen as shown below. This screen is working just fine. Today I started REDUX implementation.
const RequestScreen = ({ navigation }) => {
// code removed for brevity
}
Within this screen I use the useEffect hook to fetch data from my API
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
getServices();
});
return () => {
unsubscribe;
};
}, [navigation]);
const getServices = async () => {
setLoading(true);
// helper function to call API
await getAllServices().then((response) => {
if (response !== undefined) {
const services = response.service;
setServices(services);
setLoading(false);
}
});
// update redux state
props.getAllServices(services);
};
// code removed for brevity
const mapStateToProps = (state) => state;
const mapDispatchToProps = (dispatch) => ({
getServices: (services) =>
dispatch({
type: Types.GET_SERVICES,
payload: { services },
}),
});
const connectComponent = connect(mapStateToProps, mapDispatchToProps);
export default connectComponent(RequestScreen);
On this line of code:
props.getAllServices(services);
I keep getting this error:
[Unhandled promise rejection: TypeError: undefined is not an object
(evaluating 'props.getAllServices')] at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:293:29 in invoke
Anytime I try to use "props" in code here. I run into errors. How do I get the props on this screen?
I tried changing the screen, as shown below, but that does not work either!
const RequestScreen = ({ navigation, props }) => {
// code removed
}
I was able to get the props object after changing the screen component as shown below.
const RequestScreen = ({ navigation, ...props }) => {}
I would like to write tests for my React-native app. My parent component will execute the methods within the child component.
My child component is using the Hooks forwardRef, useImperativeHandle, Ref as seen below
childs.tsx
export interface RefChild {
toggle: () => void,
close: () => void
}
const Child = forwardRef((props: ChildProps, ref: Ref<RefChild>) => {
const [isVisible, setIsVisible] = useState(false);
useImperativeHandle(ref, () => ({ toggle, close }));
const toggle = () => {
setIsVisible(!isVisible);
}
const close = () => {
setIsVisible(false)
}
return (...mycomponent)
}
My Parent component is catching the 'ref' call with
ref={(el: RefChild) => childRef.current = el}
Which allows me to call the 'toggle' and 'close' methods from within the Parent.
Now, I fail to understand how to do the same thing within my test
my parent-test.tsx:
describe('Parent', () => {
let wrapper: ShallowWrapper;
let props: any;
beforeEach(() => {
props = createTestProps({});
wrapper = shallow(<Parent {...props} />);
});
//this is what I am currently trying to do, but not working
//test 1 (not working)
it("useRef child", () => {
const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: <Child/> });
expect(useRefSpy).toBeCalled();
useRefSpy.current.toggle();
})
//test 2 (not working)
it("useRef child2", () => {
const ref = {
current: {
toggle: jest.fn(),
close: jest.fn()
}
}
ref.current.toggle();
})
//test 3 (not working)
it("useRef child3", () => {
wrapper.instance().childref.current.toggle(); //failing as functional components don't have instance
})
})
My versions of React and RN are:
"react": "16.13.1",
"react-native": "0.63.3"
Could anyone explain me how should I achieve this?
As you mentioned in your question there is no instance in functional component, I think there is a better way to handle toggle and close functions from parent component using a boolean prop for each of them and the listen to changes in this value like this:
you have a state in parent component called isClose set to false and then in child component you use something like this:
useEffect(() => {
if(isClose){
//call close function
close()
}
}, [isClose])
But by the way in your current setup I think you need to mock the useRef hook something like this:
const useRefSpy = jest
.spyOn(React, "useRef")
.mockReturnValueOnce(() => ({ current: <Child /> }));