react hooks invalid call hooks - react-hooks

Hello i'm trying to load a style into my ui stuff but i'm having trouble doing that
const useStyles = makeStyles(loginPage)
const classes = useStyles();
const renderTextField = ({
label,
input,
meta: { touched, invalid, error },
...custom
}) => (
<TextField
label={label}
placeholder={label}
variant="outlined"
InputLabelProps={{
classes: {
root: classes.label,
focused: classes.focusedLabel,
error: classes.erroredLabel
}
}}
InputProps={{
classes: {
root: classes.cssOutlinedInput,
focused: classes.cssFocused,
notchedOutline: classes.notchedOutline,
},
startAdornment: (
<InputAdornment position="start">
<PersonSharpIcon style={{ fontSize: 25 , color: 'rgba(20, 176, 12,0.9)' }} />
</InputAdornment>
)
}}
error={touched && invalid}
helperText={touched && error}
{...input}
{...custom}
/>
)
error:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component.
Could someone help me how I would solve this?

It's exactly as the error message says. You need to move your hook inside the body of your function component.
React considers every function starting with 'use' as a hook. So in your case it's useStyles(). React also expects such functions to be called only from inside of the body of function components and only from the root of it (so nesting it inside loops or conditional statements is a big no - you can read about it here). Your function component is renderTextField, so as you can see you're calling useStyles() OUTSIDE of renderTextField's body.
Structuring it something like this should help:
const useStyles = makeStyles(loginPage);
const RenderTextField = ({
label,
input,
meta: { touched, invalid, error },
...custom
}) => {
const classes = useStyles(); // <-- Move it here
return (
<TextField
label={label}
...
>
...
</TextField>
);
}

Related

React Navigation 6 attempting to pass different params to same screen

I currently have the following setup an onboarding stack component:
export default function OnboardingStack(props) {
const { auth, activeUser } = useContext(FirebaseContext);
return (
<Stack.Navigator mode="card" headerMode="none">
<Stack.Screen
name="Login"
component={Login}
option={{
headerTransparent: true
}}
/>
<Stack.Screen name="App" component={AppStack} />
</Stack.Navigator>
);
}
Then I can call a component called MemberList which contains a touchable opacity:
<TouchableOpacity style={styles.touchableRow} onPress={ () => navigateMember(item) }>
the method navigateMember I navigate to "Add Member"
const navigateMember = (item) => {
navigation.navigate("Add Member", {
screen: "Member",
params: {
uid: item,
}
}
);
}
Here item is different each time I click it but when I get into the "Member" screen it retains the first original passed uid. Member component contains:
useEffect(() => {
navigation.addListener('focus', () => {
// console.log(navigation);
console.log('this member route');
console.log(route);
})
navigation.addListener('blur', () => {
console.log('leaving blur');
navigation.setParams({
key: route.key,
params: { uid: 'og' },
})
})
}, [])
Each time the uid remains the same. I've tried to reset it when it blurs but it always retains the same params. Any idea what I'm doing wrong?
The solution was to use push as opposed to navigate.
onPress={() => navigation.push('Member')}
More details found in the documentation

i18next with HttpBackend, Trans looking for key before init finished

I'm loading translations dynamically, with the HttpBackend
However inside my component I use the Trans component, and it is screaming about missing translation key, I also see the init finished after it tried to access the Trans component.
I have a Suspens around my app, why is this happens?
I get in the console:
i18next::translator: missingKey en translation myKey This is text that has a in it.... 
the init happens after.
How can I fix this?
// file: i18n.js
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
debug: true,
supportedLngs: ['en', 'bg', 'rs'],
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
backend: {
loadPath: '/static/{{lng}}.translations.json',
},
react: {
wait: true,
useSuspense: true,
},
transSupportBasicHtmlNodes: true,
});
export default i18n;
// app.js
function App() {
return (
<BrowserRouter>
<Apollo>
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<Header />
<Routes />
<Footer />
</ThemeProvider>
</Suspense>
</Apollo>
</BrowserRouter>
);
}
problematic component:
//home
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
You should pass the t function to Trans component.
//home
import { useTranslation } from 'react-i18next';
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => {
const { t } = useTranslation();
return (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT} t={t}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
};
The solution which is sadly not document is:
const {t} = useTranstions()
<Trans i18nKey="someKey" t={t}>
Passing the t into the Trans works perfectly.

Invalid hook call. React hooks

error:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons
Hello I am trying to use useDispatch in my action but it is generating this error from invalid hoook
I can't solve it
can anybody help me?
my action
import {FETCH_FAIL,FETCH_LOADING,FETCH_SUCESS} from './actionType';
import api from '../../../services/api';
import { useDispatch } from "react-redux";
const FetchSucess = data => (console.log(data),{
type:FETCH_SUCESS,
data
});
const FetchFailed = error => ({
type:FETCH_FAIL,
error
});
const isLoadingFetch = () => ({type: FETCH_LOADING})
export default function AllProducts () {
const dispatch = useDispatch()
dispatch(isLoadingFetch());
// fetching data
api.get('/products')
.then( response => { dispatch(FetchSucess(response.data))})
.catch( err => { dispatch(FetchFailed(err.message));});
}
my component
import React, { useState, useEffect } from 'react';
export default function Cards() {
useEffect(() => {
// This will be invoked only once.
getAllProducts();
}, []);
const classes = useStyles();
const classes2 = useStyles2();
const products = useSelector(state => state.data.filteredProducts);
return (
<div className="App">
<Container maxWidth="md" className={classes.root}>
<Grid container md={4} spacing={1} ></Grid>
<Grid container md={8} spacing={1} alignItems={"center"}>
{products.map(product => (
<Grid item lg={4} md={4} sm={12} xs={12}>
<Card className={classes2.card}>
<CardMedia
className={classes2.media}
image={
"https://www.theclutch.com.br/wp-content/uploads/2019/11/skins-csgo-neymar.jpg"
}
/>
<CardContent className={classes2.content}>
<Typography
className={classes2.name}
variant={"h6"}
gutterBottom
>
{product.name}
</Typography>
<Typography
className={classes2.price}
variant={"h1"}
>
{util.formatCurrency(product.price)}
</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</Container>
</div>
);
}
Based on this comment above:
All product is my action
If AllProducts is a Redux action that needs to perform an async operation and dispatch other actions in response to that operation, there's a convention available by which Redux will pass dispatch as a function argument. The action just needs to return a function which accepts that argument. For example:
export default function AllProducts () {
return function(dispatch) {
dispatch(isLoadingFetch());
// fetching data
api.get('/products')
.then( response => { dispatch(FetchSucess(response.data))})
.catch( err => { dispatch(FetchFailed(err.message));});
}
}
There's no need to use the hook, that's only necessary within React Function Components or within other hooks (which themselves are used within React Function Components).

Are dynamic mocks possible in storybooks using Apollo's MockedProvider?

In my React storybooks, I want to be able to toy around with components that use graphQL queries and mutations (implemented with Apollo).
This works fine using MockedProvider, as long as I specify in-advance the exact mutations, including their inputs.
I want to know if it is possible/how to not specify the inputs in advance, to accept any inputs.
export const MyComponent = () => (
<Mutation mutation={gql`some mutation`}>
{(doMutation, { loading, error, data }) => (
<Button onClick={()=> doMutation({input: {
someInput: Math.rand()*10 // Would be fine if this was 1.
}}) />
{data ? <>Result: {data.someResult}</> : null}
)
</Mutation>
)
storiesOf('MyComponent', module)
.add('some story', () => (
<StaticRouter context={{}}>
<MockedProvider
mocks={[
{
request: {
query: gql`some query...`,
variables: { input: { someInput: '1' } },
},
result: { data: { someResult: '1' } },
},
]}
addTypename={true}
>
<MyComponent />
</MockedProvider>
</StaticRouter>
))
In the pseudo-example above, the storybook will work fine if I send '1' as my input, but will not work for any other number - the mock must match exactly or I get "no more mocked responses for someMutation with variables {...}".
This is not a problem in tests, but in storybooks it'd be nice to be able to test with any values.
I found a way to do what I wanted, albeit not using MockedProvider.
First, convert to hooks.
export const MyComponent = () => {
const [doMutation, {loading, data, error}] = useMutation(gql`some mutation`)
return (
<Button onClick={()=> doMutation({input: {
someInput: Math.rand()*10 // Would be fine if this was 1.
}}) />
{data ? <>Result: {data.someResult}</> : null}
);
}
Now dependency-inject the mutation hook, using react-magnetic-di
export const useMyMutation = () => useMutation(gql`some mutation`)
export const MyComponent = () => {
di(useMyMutation)
const [doMutation, {loading, data, error}] = useMyMutation() // Will be the in-scope variable `useMyMutation` from above unless expressly injected during stories/tests.
return (
<Button onClick={()=> doMutation({input: {
someInput: Math.rand()*10 // Would be fine if this was 1.
}}) />
{data ? <>Result: {data.someResult}</> : null}
);
}
Now you can write stories/tests with custom implementations.
export const useMockMyMutation = injectable(
myMutation, // imported from the component, types are checked against it.
() => [function thisWillBeDoMutation(){}, {loading: false, data: {}, error: null}])
storiesOf('MyComponent', module)
.add('some story', () => (
<StaticRouter context={{}}>
<DiProvider target={MyComponent} use={[useMockMyMutation]}>
<MyComponent />
</DiProvider>
</StaticRouter>
))
So we directly supply any function for the doMutation, and you can make that change the values used for loading/data/error etc. if you need to.
We've made a lot of tooling to make it a bit more streamlined, so extracting this example was a bit tricky so you'll need to do a bit of playing around with react-magnetic-di; but this is the gist of it.
Good luck!

Console error whilst waiting for API response - React/Redux

I am fetching data from a remote API in componentDidMount:
componentDidMount() {
this.props.fetchRemoteData('photos')
}
And then the received data is passed to my component props in mapStateToProps, using a selector to filter a specific object from the received array:
const mapStateToProps = (state, { params }) => {
const photoId = parseInt(params.photoId)
return {
singlePhoto: getSinglePhoto(state.filteredList.photos.jsonArray, photoId),
isFetching: state.filteredList.photos.isFetching
}
}
The content renders, but there is a split second before that, where it seems to be trying to the render the content before the data is successfully retrieved, which brings up the following error in the console:
Uncaught TypeError: Cannot read property 'charAt' of undefined
undefined is here referring to this.props.singlePhoto. But when singlePhoto receives the data payload the content renders.
Here is my container component:
class PhotoSingle extends Component {
componentDidMount() {
this.props.fetchRemoteData('photos')
}
render() {
const {singlePhoto, isFetching} = this.props
const photoTitle = capitalizeFirstLetter(singlePhoto.title)
return (
<div>
<PhotoSingleImg singlePhoto={singlePhoto} photoTitle={photoTitle} isFetching={isFetching}/>
</div>
)
}
}
const mapStateToProps = (state, { params }) => {
const photoId = parseInt(params.photoId)
return {
singlePhoto: getSinglePhoto(state.filteredList.photos.jsonArray, photoId),
isFetching: state.filteredList.photos.isFetching
}
}
import * as actions from '../actions/actionCreators'
PhotoSingle = connect(mapStateToProps, actions)(PhotoSingle)
export default PhotoSingle;
And my presentational component:
const PhotoSingleImg = ({ singlePhoto, photoTitle, isFetching }) => {
if (isFetching) {
return <h4>Fetching data...</h4>
}
return (
<div>
<h1>Single Photo</h1>
<h3>Title</h3>
<hr />
<img className='single-photo' src={singlePhoto.url} />
<p>Album ID: {singlePhoto.albumId} | Photo ID: {singlePhoto.id}</p>
</div>
)
}
export default PhotoSingleImg;
I'm unsure how to make it so the content will only attempt to render after I the API response has been received.
Any help appreciated.
Have you defined initial state in redux store?
You can try this way:
return singlePhoto ?
(<div>
<h1>Single Photo</h1>
<h3>Title</h3>
<hr />
<img className='single-photo' src={singlePhoto.url} />
<p>Album ID: {singlePhoto.albumId} | Photo ID: {singlePhoto.id}</p>
</div>) : null

Resources