Quantcast
Channel: Active questions tagged react-native+typescript - Stack Overflow
Viewing all articles
Browse latest Browse all 6287

Why cant use redux hooks at child component?

$
0
0

I've same error of that ask,i've a Redux provider from a react-native app,i can use Redux hooks (useSelector, useDispatch) from a component but in child of same componenti cant use Redux hooks with error message:

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

With this call stack:

This error is located at:    in MusicDetails (created by SearchItemDetails) <- Error component try call: useDispatch    in RCTView (created by View)    in View (created by AnimatedComponent)    in AnimatedComponent    in AnimatedComponentWrapper (created by Surface)    in Surface    in ThemedComponent (created by withTheme(Surface))    in withTheme(Surface) (created by SearchItemDetails)    in RCTView (created by View)    in View (created by AnimatedComponent)    in AnimatedComponent    in AnimatedComponentWrapper (created by Surface)    in Surface    in ThemedComponent (created by withTheme(Surface))    in withTheme(Surface) (created by Modal)    in RCTView (created by View)    in View (created by Modal)    in RCTView (created by View)    in View (created by AnimatedComponent)    in AnimatedComponent    in AnimatedComponentWrapper (created by Modal)    in Modal (created by SearchItemDetails) <- Parent component can use hooks Redux    in ThemeProvider <- Theme Provider    in RCTView (created by View)    in View (created by PortalManager)    in PortalManager (created by Portal.Host)    in Portal.Host (created by Provider)    in Provider (created by App) <- Redux Provider    in App (created by ExpoRoot)    in ExpoRoot    in RCTView (created by View)    in View (created by AppContainer)    in RCTView (created by View)    in View (created by AppContainer)    in AppContainer    in main(RootComponent)

component App.tsx (top level Root):

import React from 'react';import { StatusBar } from 'expo-status-bar';import DatabaseContext, {database} from './src/Context/DatabaseContext';import createSqliteTable from './src/libs/create-sqlite-tables';import Routing from './src/components/Routing';import { Provider as ThemeProvider, DarkTheme, ActivityIndicator } from 'react-native-paper';import {Provider as ReduxProvider} from 'react-redux';import store from './src/store'import { View } from 'react-native';export default function App() {  const [isPrePending, setIsPrePending] = React.useState(true);  const isPrePendingError = React.useRef<boolean>(false);  React.useEffect(() => {    createSqliteTable(database)    .catch(error => {      isPrePendingError.current = true;      console.log("> create SQLite tables has fail with: ", error);    })    .then(() => {      console.log("> SQLite tables has been created.")    })    .finally(() => {      setIsPrePending(false);    })  }, []);  return (<><StatusBar style="auto" translucent={false} /><DatabaseContext.Provider value={database}><ThemeProvider theme={DarkTheme}><ReduxProvider store={store}>          {isPrePending ? (<View style={{              flex: 1,              display: "flex",              alignItems: "center",              justifyContent: "center"            }}><ActivityIndicator animating size={32} /></View>          ): <Routing />}</ReduxProvider></ThemeProvider></DatabaseContext.Provider></>  );}

the SearchItemDetails component:

  • child of Routing component
  • parent of MusicDetails component
import * as React from 'react';import { Modal, Portal, Surface } from 'react-native-paper';import type { AlbumPreviewAPI, ArtistPreviewAPI, MusicVideoAPI } from '../../api/ytm-api';import { useAppDispatch, useAppSelector } from '../../hooks/redux';import { createCancel } from '../../store/actions/searchDetailsActions';import { SearchDetailsState } from '../../store/reducers/searchDetailsReducers';import AlbumDetails from '../AlbumDetails/AlbumDetails';import ArtistDetails from '../ArtistDetails/ArtistDetails';import MusicDetails from '../MusicDetails/MusicDetails';import styles from '../styles';interface SearchItemDetailsProps {}const SearchItemDetails: React.FC<SearchItemDetailsProps> = () => {  const searchDetails = useAppSelector(state => state.searchDetails); // hooks work  const dispatch = useAppDispatch(); // hooks work  const isAlbum = (details: SearchDetailsState): details is AlbumPreviewAPI => (    !!(details as AlbumPreviewAPI)?.albumId  );  const isArtist = (details: SearchDetailsState): details is ArtistPreviewAPI => (    !!(details as ArtistPreviewAPI)?.artistId  );  const isMusic = (details: SearchDetailsState): details is MusicVideoAPI => (    !!(details as MusicVideoAPI)?.youtubeId  );  const getComponent = (details: SearchDetailsState | null = null): React.ElementType<{    onClose: () => void;    item: any  }> | never => {    if(!details) {      return React.Fragment;    }    if(isAlbum(details)) {      return AlbumDetails;    } else if(isArtist(details)) {      return ArtistDetails;    } else if(isMusic(details)) {      return MusicDetails;    } else {      throw new Error(`cant render SearchItemDetails because never find item category`);    }  }  const Component = getComponent(searchDetails);  const onCancelDetails = () => {    dispatch(createCancel());  }  return (<Portal><Modal visible={!!searchDetails} onDismiss={onCancelDetails}><Surface style={styles.modalContainer}>          {!!searchDetails && <Component onClose={onCancelDetails} item={searchDetails} />}</Surface></Modal></Portal>  );}export default SearchItemDetails;

And the MusicDetails component (cant use Redux hooks):

  • child of SearchItemDetails
import * as React from 'react';import { ActivityIndicator, Image, View } from 'react-native';import { IconButton } from 'react-native-paper';import { useAppDispatch } from '../../hooks/redux';import useSelectPlaylist from '../../hooks/useSelectPlaylist';import { createAddDownload } from '../../store/actions/downloadReducers';import ArtistInline from '../ArtistInline/ArtistInline';import ModalHeader from '../ModalHeader/ModalHeader';import api, {ArtistAPI, GetArtistAPI, MusicVideoAPI} from './../../api/ytm-api';interface MusicDetailsProps {  item: MusicVideoAPI;  onClose: () => void;}const MusicDetails: React.FC<MusicDetailsProps> = ({  item,  onClose}) => {  const dispatch = useAppDispatch(); // NOT WORK!!  // Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>  const artistsRef = React.useRef<ArtistAPI[]>([]);  const abortArtists = React.useRef<AbortController>(new AbortController());  const { onOpen, playlist, render } = useSelectPlaylist({    musicTitle: item.title || ""  })  const [isPendingArtists, setIsPendingArtists] = React.useState<boolean>(false);  React.useEffect(() => {    if(!item.artists?.length) {      return;    }    setIsPendingArtists(true);    abortArtists.current = new AbortController();    Promise.all(item.artists.map(artist => (      api.getArtist(artist.id || "", {signal: abortArtists.current.signal})    )))    .then((responses: GetArtistAPI[]) => {      artistsRef.current = responses.map(response => response.artist);    })    .catch(firstError => {      console.log(`> cant fetch artists with ${firstError.code} => ${firstError.message}`);    })    .finally(() => {      setIsPendingArtists(false);    })    return () => {      abortArtists.current.abort();    }  }, [item]);  React.useEffect(() => {    if(playlist !== null) {      onDownload(playlist);    }  }, [playlist])  const onDownload = (playlist: {id: number, name: string}) => {    console.log(`@TODO: dispatch download with (      youtubeId: ${item.youtubeId},      ownerName: ${artistsRef?.current[0].name},      remote: ${api.getStreamUrl(item.youtubeId || "")},      title: ${item.title},      thumbnail: ${item.thumbnailUrl},      playlistId: ${playlist.id},      ownerThumbnail: ${(artistsRef?.current[0].thumbnails && artistsRef?.current[0].thumbnails.length > 0 ) ? artistsRef?.current[0].thumbnails[0].url: undefined}    )`)    // dispatch(createAddDownload({    //   youtubeId: item.youtubeId || "",    //   ownerName: artistsRef?.current[0].name || "",    //   remote: api.getStreamUrl(item.youtubeId || ""),    //   title: item.title || "",    //   thumbnail: item.thumbnailUrl,    //   playlistId: playlist.id,    //   ownerThumbnail: (artistsRef?.current[0].thumbnails && artistsRef?.current[0].thumbnails.length > 0 ) ? artistsRef?.current[0].thumbnails[0].url: undefined    // }))  }  return (<><ModalHeader title={item.title || ""} subtitle={item.album} onClose={onClose} /><View style={{marginVertical: 4}}><View style={{        display: "flex",        alignItems: "center",        justifyContent: "center",        flexDirection: "row",      }}><View style={{          position: "relative"        }}><Image style={{width: 96, height: 96}} source={{uri: item.thumbnailUrl}} /><View style={{            position: "absolute",            left: 0,            right: 0,            bottom: 0,            top: 0,            backgroundColor: "rgba(0,0,0,.6)",            display: "flex",            alignItems: "center",            justifyContent: "center"          }}><IconButton icon="download" size={24} onPress={onOpen} /></View></View></View><View>        {isPendingArtists ? (<View style={{            flex: 1,            justifyContent: "center",            alignItems: "center",            marginVertical: 4          }}><ActivityIndicator animating size={16} /></View>        ): (<View style={{            marginVertical: 4          }}>            {artistsRef.current?.map(artist => (<ArtistInline artist={artist} key={artist.artistId} />            ))}</View>        )}</View></View>    {render}</>  );}export default MusicDetails;

From my current structure my component MusicDetails is really a child of ReduxProvider,why i cant use redux hooks from this component ?

Redux hooks are imports from local file because i've import default hook and re export with type hint:

hooks/redux.ts:

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'import type { RootState, AppDispatch } from './../store'// Use throughout your app instead of plain `useDispatch` and `useSelector`export const useAppDispatch: () => AppDispatch = useDispatchexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

I've just follow documentation about using with typescript for that.


Viewing all articles
Browse latest Browse all 6287

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>