Replacement for redux hook inside a function - reactjs

I really need to have a global state inside the below function but I'm getting this error:
Invalid hook call. Hooks can only be called inside of the body of a function component
Is there a workaround or other solution for this?
import useVoiceHook from '../../hooks/voicehook';
const Sound = require('react-native-sound');
function PlaySound(name) {
const voicehook = useVoiceHook();
const voice = new Sound(name, Sound.MAIN_BUNDLE, async (error) => {
if (error) {
console.warn('failed to load the sound', error);
return;
}
if (voicehook.data.lastVoice) {
voicehook.data.lastVoice.stop();
}
voicehook.setVoice(voice);
voice.play();
});
}
export default PlaySound;

Since you try to use the PlaySound function on an event it throws you the error because you can't use a hook within it. Hooks are meant to be used as top level functions within a functional component.
Check the Rules of hooks for more details
You should pass the voicehook value as argument to playSound function and consume the same
function MyComponent(props) {
const voicehook = useVoiceHook();
...
return (
<TouchableOpacity style={{backgroundColor: "red", padding: 20}} onPress={()=> {
PlaySound('click', voicehook)
}
}>
<Text>X</Text>
</TouchableOpacity>
)
}
function PlaySound(name, voicehook) {
const voice = new Sound(name, Sound.MAIN_BUNDLE, async (error) => {
if (error) {
console.warn('failed to load the sound', error);
return;
}
if (voicehook.data.lastVoice) {
voicehook.data.lastVoice.stop();
}
voicehook.setVoice(voice);
voice.play();
});
}
export default PlaySound;

Related

In my React App getting firebase Google login Warning in the console, how can I fix this Warning? [duplicate]

I am getting this warning in react:
index.js:1 Warning: Cannot update a component (`ConnectFunction`)
while rendering a different component (`Register`). To locate the
bad setState() call inside `Register`
I went to the locations indicated in the stack trace and removed all setstates but the warning still persists. Is it possible this could occur from redux dispatch?
my code:
register.js
class Register extends Component {
render() {
if( this.props.registerStatus === SUCCESS) {
// Reset register status to allow return to register page
this.props.dispatch( resetRegisterStatus()) # THIS IS THE LINE THAT CAUSES THE ERROR ACCORDING TO THE STACK TRACE
return <Redirect push to = {HOME}/>
}
return (
<div style = {{paddingTop: "180px", background: 'radial-gradient(circle, rgba(106,103,103,1) 0%, rgba(36,36,36,1) 100%)', height: "100vh"}}>
<RegistrationForm/>
</div>
);
}
}
function mapStateToProps( state ) {
return {
registerStatus: state.userReducer.registerStatus
}
}
export default connect ( mapStateToProps ) ( Register );
function which triggers the warning in my registerForm component called by register.js
handleSubmit = async () => {
if( this.isValidForm() ) {
const details = {
"username": this.state.username,
"password": this.state.password,
"email": this.state.email,
"clearance": this.state.clearance
}
await this.props.dispatch( register(details) )
if( this.props.registerStatus !== SUCCESS && this.mounted ) {
this.setState( {errorMsg: this.props.registerError})
this.handleShowError()
}
}
else {
if( this.mounted ) {
this.setState( {errorMsg: "Error - registration credentials are invalid!"} )
this.handleShowError()
}
}
}
Stacktrace:
This warning was introduced since React V16.3.0.
If you are using functional components you could wrap the setState call into useEffect.
Code that does not work:
const HomePage = (props) => {
props.setAuthenticated(true);
const handleChange = (e) => {
props.setSearchTerm(e.target.value.toLowerCase());
};
return (
<div key={props.restInfo.storeId} className="container-fluid">
<ProductList searchResults={props.searchResults} />
</div>
);
};
Now you can change it to:
const HomePage = (props) => {
// trigger on component mount
useEffect(() => {
props.setAuthenticated(true);
}, []);
const handleChange = (e) => {
props.setSearchTerm(e.target.value.toLowerCase());
};
return (
<div key={props.restInfo.storeId} className="container-fluid">
<ProductList searchResults={props.searchResults} />
</div>
);
};
I just had this issue and it took me a bit of digging around before I realised what I'd done wrong – I just wasn't paying attention to how I was writing my functional component.
I was doing this:
const LiveMatches = (props: LiveMatchesProps) => {
const {
dateMatches,
draftingConfig,
sportId,
getDateMatches,
} = props;
if (!dateMatches) {
const date = new Date();
getDateMatches({ sportId, date });
};
return (<div>{component stuff here..}</div>);
};
I had just forgotten to use useEffect before dispatching my redux call of getDateMatches()
So it should have been:
const LiveMatches = (props: LiveMatchesProps) => {
const {
dateMatches,
draftingConfig,
sportId,
getDateMatches,
} = props;
useEffect(() => {
if (!dateMatches) {
const date = new Date();
getDateMatches({ sportId, date });
}
}, [dateMatches, getDateMatches, sportId]);
return (<div>{component stuff here..}</div>);
};
please read the error message thoroughly, mine was pointing to SignIn Component that had a bad setState. which when i examined, I had an onpress that was not an Arrow function.
it was like this:
onPress={navigation.navigate("Home", { screen: "HomeScreen" })}
I changed it to this:
onPress={() => navigation.navigate("Home", { screen: "HomeScreen" }) }
My error message was:
Warning: Cannot update a component
(ForwardRef(BaseNavigationContainer)) while rendering a different
component (SignIn). To locate the bad setState() call inside
SignIn, follow the stack trace as described in
https://reactjs.org/link/setstate-in-render
in SignIn (at SignInScreen.tsx:20)
I fixed this issue by removing the dispatch from the register components render method to the componentwillunmount method. This is because I wanted this logic to occur right before redirecting to the login page. In general it's best practice to put all your logic outside the render method so my code was just poorly written before. Hope this helps anyone else in future :)
My refactored register component:
class Register extends Component {
componentWillUnmount() {
// Reset register status to allow return to register page
if ( this.props.registerStatus !== "" ) this.props.dispatch( resetRegisterStatus() )
}
render() {
if( this.props.registerStatus === SUCCESS ) {
return <Redirect push to = {LOGIN}/>
}
return (
<div style = {{paddingTop: "180px", background: 'radial-gradient(circle, rgba(106,103,103,1) 0%, rgba(36,36,36,1) 100%)', height: "100vh"}}>
<RegistrationForm/>
</div>
);
}
}
I think that this is important.
It's from this post that #Red-Baron pointed out:
#machineghost : I think you're misunderstanding what the message is warning about.
There's nothing wrong with passing callbacks to children that update state in parents. That's always been fine.
The problem is when one component queues an update in another component, while the first component is rendering.
In other words, don't do this:
function SomeChildComponent(props) {
props.updateSomething();
return <div />
}
But this is fine:
function SomeChildComponent(props) {
// or make a callback click handler and call it in there
return <button onClick={props.updateSomething}>Click Me</button>
}
And, as Dan has pointed out various times, queuing an update in the same component while rendering is fine too:
function SomeChildComponent(props) {
const [number, setNumber] = useState(0);
if(props.someValue > 10 && number < 5) {
// queue an update while rendering, equivalent to getDerivedStateFromProps
setNumber(42);
}
return <div>{number}</div>
}
If useEffect cannot be used in your case or if the error is NOT because of Redux
I used setTimeout to redirect one of the two useState variables to the callback queue.
I have one parent and one child component with useState variable in each of them. The solution is to wrap useState variable using setTimeout:
setTimeout(() => SetFilterData(data), 0);
Example below
Parent Component
import ExpenseFilter from '../ExpensesFilter'
function ExpensesView(props) {
const [filterData, SetFilterData] = useState('')
const GetFilterData = (data) => {
// SetFilterData(data);
//*****WRAP useState VARIABLE INSIDE setTimeout WITH 0 TIME AS BELOW.*****
setTimeout(() => SetFilterData(data), 0);
}
const filteredArray = props.expense.filter(expenseFiltered =>
expenseFiltered.dateSpent.getFullYear().toString() === filterData);
return (
<Window>
<div>
<ExpenseFilter FilterYear = {GetFilterData}></ExpenseFilter>
Child Component
const ExpensesFilter = (props) => {
const [filterYear, SetFilterYear] = useState('2022')
const FilterYearListener = (event) => {
event.preventDefault()
SetFilterYear(event.target.value)
}
props.FilterYear(filterYear)
return (
Using React and Material UI (MUI)
I changed my code from:
<IconButton onClick={setOpenDeleteDialog(false)}>
<Close />
</IconButton>
To:
<IconButton onClick={() => setOpenDeleteDialog(false)}>
<Close />
</IconButton>
Simple fix
If you use React Navigation and you are using the setParams or setOptions you must put these inside method componentDidMount() of class components or in useEffects() hook of functional components.
Minimal reproducing example
I was a bit confused as to what exactly triggers the problem, having a minimal immediately runnable example helped me grasp it a little better:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone#7.14.7/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function NotMain(props) {
props.setN(1)
return <div>NotMain</div>
}
function Main(props) {
const [n, setN] = React.useState(0)
return <>
<NotMain setN={setN} />
<div>Main {n}</div>
</>
}
ReactDOM.render(
<Main/>,
document.getElementById('root')
);
</script>
</body>
</html>
fails with error:
react-dom.development.js:61 Warning: Cannot update a component (`Main`) while rendering a different component (`NotMain`). To locate the bad setState() call inside `NotMain`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
followed by a stack trace:
at NotMain (<anonymous>:16:9)
at Main (<anonymous>:21:31)
Presumably 16:9 would be the exact line where props.setN(1) is being called from, but the line numbers are a bit messed up because of the Babel JSX translation.
The solution like many other answers said is to do instead:
function NotMain(props) {
React.useEffect(() => { props.setN(1) }, [])
return <div>NotMain</div>
}
Intuitively, I think that the general idea of why this error happens is that:
You are not supposed to updat state from render methods, otherwise it could lead to different results depending on internal the ordering of how React renders things.
and when using functional components, the way to do that is to use hooks. In our case, useEffect will run after rendering is done, so we are fine doing that from there.
When using classes this becomes slightly more clear and had been asked for example at:
Calling setState in render is not avoidable
Calling setState() in React from render method
When using functional components however, things are conceptually a bit more mixed, as the component function is both the render, and the code that sets up the callbacks.
I was facing same issue, The fix worked for me was if u are doing
setParams/setOptions
outside of useEffect then this issue is occurring. So try to do such things inside useEffect. It'll work like charm
TL;DR;
For my case, what I did to fix the warning was to change from useState to useRef
react_devtools_backend.js:2574 Warning: Cannot update a component (`Index`) while rendering a different component (`Router.Consumer`). To locate the bad setState() call inside `Router.Consumer`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
at Route (http://localhost:3000/main.bundle.js:126692:29)
at Index (http://localhost:3000/main.bundle.js:144246:25)
at Switch (http://localhost:3000/main.bundle.js:126894:29)
at Suspense
at App
at AuthProvider (http://localhost:3000/main.bundle.js:144525:23)
at ErrorBoundary (http://localhost:3000/main.bundle.js:21030:87)
at Router (http://localhost:3000/main.bundle.js:126327:30)
at BrowserRouter (http://localhost:3000/main.bundle.js:125948:35)
at QueryClientProvider (http://localhost:3000/main.bundle.js:124450:21)
The full code for the context of what I did (changed from the lines with // OLD: to the line above them). However this doesn't matter, just try changing from useState to useRef!!
import { HOME_PATH, LOGIN_PATH } from '#/constants';
import { NotFoundComponent } from '#/routes';
import React from 'react';
import { Redirect, Route, RouteProps } from 'react-router-dom';
import { useAccess } from '#/access';
import { useAuthContext } from '#/contexts/AuthContext';
import { AccessLevel } from '#/models';
type Props = RouteProps & {
component: Exclude<RouteProps['component'], undefined>;
requireAccess: AccessLevel | undefined;
};
export const Index: React.FC<Props> = (props) => {
const { component: Component, requireAccess, ...rest } = props;
const { isLoading, isAuth } = useAuthContext();
const access = useAccess();
const mounted = React.useRef(false);
// OLD: const [mounted, setMounted] = React.useState(false);
return (
<Route
{...rest}
render={(props) => {
// If in indentifying authentication state as the page initially loads, render a blank page
if (!mounted.current && isLoading) return null;
// OLD: if (!mounted && isLoading) return null;
// 1. Check Authentication is one step
if (!isAuth && window.location.pathname !== LOGIN_PATH)
return <Redirect to={LOGIN_PATH} />;
if (isAuth && window.location.pathname === LOGIN_PATH)
return <Redirect to={HOME_PATH} />;
// 2. Authorization is another
if (requireAccess && !access[requireAccess])
return <NotFoundComponent />;
mounted.current = true;
// OLD: setMounted(true);
return <Component {...props} />;
}}
/>
);
};
export default Index;
My example.
Code with that error:
<Form
initialValues={{ ...kgFormValues, dataflow: dataflows.length > 0 ? dataflows[0].df_tpl_key : "" }}
onSubmit={() => {}}
render={({values, dirtyFields }: any) => {
const kgFormValuesUpdated = {
proj_key: projectKey,
name: values.name,
description: values.description,
public: values.public,
dataflow: values.dataflow,
flavours: flavoursSelected,
skipOCR: values.skipOCR
};
if (!_.isEqual(kgFormValues, kgFormValuesUpdated)) {
setNewKgFormValues(kgFormValuesUpdated);
}
Working Code:
<Form
initialValues={{ ...kgFormValues, dataflow: dataflows.length > 0 ? dataflows[0].df_tpl_key : "" }}
onSubmit={() => {}}
render={({ values, dirtyFields }: any) => {
useEffect(() => {
const kgFormValuesUpdated = {
proj_key: projectKey,
name: values.name,
description: values.description,
public: values.public,
dataflow: values.dataflow,
flavours: flavoursSelected,
skipOCR: values.skipOCR
};
if (!_.isEqual(kgFormValues, kgFormValuesUpdated)) {
setNewKgFormValues(kgFormValuesUpdated);
}
}, [values]);
return (
I had the same problem. I was setting some state that was storing a function like so:
// my state definition
const [onConfirm, setOnConfirm] = useState<() => void>();
// then I used this piece of code to update the state
function show(onConfirm: () => void) {
setOnConfirm(onConfirm);
}
The problem was from setOnConfirm. In React, setState can take the new value OR a function that returns the new value. In this case React wanted to get the new state from calling onConfirm which is not correct.
changing to this resolved my issue:
setOnConfirm(() => onConfirm);
I was able to solve this after coming across a similar question in GitHub which led me to this comment showing how to pinpoint the exact line within your file causing the error. I wasn't aware that the stack trace was there. Hopefully this helps someone!
See below for my fix. I simply converted the function to use callback.
Old code
function TopMenuItems() {
const dispatch = useDispatch();
function mountProjectListToReduxStore(projects) {
const projectDropdown = projects.map((project) => ({
id: project.id,
name: project.name,
organizationId: project.organizationId,
createdOn: project.createdOn,
lastModifiedOn: project.lastModifiedOn,
isComplete: project.isComplete,
}));
projectDropdown.sort((a, b) => a.name.localeCompare(b.name));
dispatch(loadProjectsList(projectDropdown));
dispatch(setCurrentOrganizationId(projectDropdown[0].organizationId));
}
};
New code
function TopMenuItems() {
const dispatch = useDispatch();
const mountProjectListToReduxStore = useCallback((projects) => {
const projectDropdown = projects.map((project) => ({
id: project.id,
name: project.name,
organizationId: project.organizationId,
createdOn: project.createdOn,
lastModifiedOn: project.lastModifiedOn,
isComplete: project.isComplete,
}));
projectDropdown.sort((a, b) => a.name.localeCompare(b.name));
dispatch(loadProjectsList(projectDropdown));
dispatch(setCurrentOrganizationId(projectDropdown[0].organizationId));
}, [dispatch]);
};
My case was using setState callback, instead of setState + useEffect
BAD ❌
const closePopover = useCallback(
() =>
setOpen((prevOpen) => {
prevOpen && onOpenChange(false);
return false;
}),
[onOpenChange]
);
GOOD ✅
const closePopover = useCallback(() => setOpen(false), []);
useEffect(() => onOpenChange(isOpen), [isOpen, onOpenChange]);
I got this when I was foolishly invoking a function that called dispatch instead of passing a reference to it for onClick on a button.
const quantityChangeHandler = (direction) => {
dispatch(cartActions.changeItemQuantity({title, quantityChange: direction}));
}
...
<button onClick={() => quantityChangeHandler(-1)}>-</button>
<button onClick={() => quantityChangeHandler(1)}>+</button>
Initially, I was directly calling without the fat arrow wrapper.
Using some of the answers above, i got rid of the error with the following:
from
if (value === "newest") {
dispatch(sortArticlesNewest());
} else {
dispatch(sortArticlesOldest());
}
this code was on my component top-level
to
const SelectSorting = () => {
const dispatch = useAppDispatch();
const {value, onChange} = useSelect();
useEffect(() => {
if (value === "newest") {
dispatch(sortArticlesNewest());
} else {
dispatch(sortArticlesOldest());
}
}, [dispatch, value]);

How can I use 'useQuery' as a callback function for a click event in React?

Please understand that I am not fluent in English.
How can I use 'useQuery' as a callback function for a click event in React?
The error contents are as follows.
'Error: Invalid hook call. Hooks can only be called inside of the body of a function component.'
const createGuestBookWrite = () => {
const { data, error } = useQuery(GuestApi.ACTIONS.CREATE_GUEST_BOOK, async () => {
return await GuestApi.fetchGuestBookWrite({
spaceUid: 2,
bookText: content,
});
});
console.log('data::::::::::', data);
};
<span onClick={createGuestBookWrite}>
<TextButton textID={I18N_CODE.GUESTBOOK_NEW_PUBLICATION}></TextButton>
</span>

Rendering data from fetch request React Native

I'm trying to use data from a fetch request but it doesn't load even after some time. How can I get my return function to display the instagram function data? The useEffect hook isn't getting the data to load.
export default function Dashboard(props) {
const [instagram, setInstagram] = useState('');
useEffect(() => {
var url = "https://graph.instagram.com/me/media?fields=media_url,media_type,permalink&access_token=IGQVJHB6RAZDZD"
fetch(url)
.then(response => response.json())
.then((user) => {
console.log("INSTA", user.data);
setInstagram(user.data)
});
},[])
var pics = function(instagram) {
if(instagram){
console.log("found")
instagram.map(function(item, i){
return <li>Pic</li>
})
}
}
return (
<View id="instafeed" style={styles.instagram}></View>
{
pics()
}
</View>
)
}
}
Your pics function doesn't return anything, add this return statement.
var pics = function(instagram) {
if(instagram){
console.log("found")
return instagram.map(function(item, i){
return <li>Pic</li>
})
}
}
Your problem is you re-declare instagram as a parameter of the pics function, then call the pics function without any argument.
The code inside your pics function refers to the instagram from the parameter, not the parent scope instagram and as you called the pics function without any argument, will always be undefined.
To fix your code, either removing instagram parameter of the pics function, or to call pics function with the state.
For example:
...
var pics = function() {
if(instagram){
console.log("found")
instagram.map(function(item, i){
return <li>Pic</li>
})
}
}
...
Or
return (
<View id="instafeed" style={styles.instagram}></View>
{
pics(instagram)
}
</View>
)
I reckon it will be beneficial if you do some research on the topic of Javascript closure and lexical scope.

Invalid hook call in a react function, ImagePicker and ActionSheet

I really need some help with hooks in react native...
I have an ActionSheet which onPress one of the options a camera feature should open with "useCamera" function.
The "useCamera" is in another function(on another file),
using the library "ImagePicker". (specific "ImagePicker.launchCamera")
the "ImagePicker.launchCamera" set the fileUri in the callback while the function return nothing.
So to access the fileUri, I tried to use useState Hook but I get "ERROR: Invalid Hook call.Hooks can only be called inside of the body of the function..."
but the hook inside the function bodY!
ActionSheet code:
import {useCamera} from './PhotoHandle';
// export default class EditProfilePic extends React.Component {
function EditProfilePic(props) {
const [ActionSheetRef, setActionSheetRef] = useState(null);
const [fileDate, setFileDate] = useState(null);
return (
<View style={styles.images_container}>
<TouchableOpacity
style={styles.click_edit_icon}
onPress={() => ActionSheetRef.show()}>
<Image style={styles.editIcon} source={editIcon} />
</TouchableOpacity>
<Image source={props.profileSource} style={styles.image} />
<ActionSheet
ref={o => setActionSheetRef(o)}
title={
<Text style={{color: '#000', fontSize: 18}}>
Which one do you like?
</Text>
}
options={['Select from Photos', 'Camera', 'Cancel']}
cancelButtonIndex={2}
destructiveButtonIndex={2}
onPress={index => {
//camera
if (index == 1) {
useCamera()
}
}}
/>
</View>
);
}
LaunchCamera function:
export function useCamera() {
const [fileData, setFileData] = useState(null);
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.launchCamera(options, response => {
console.log('coolio' + response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
alert(response.customButton);
} else {
setFileData(response.data);
}
});
return fileData;
}
I tried to call useCamera() like "< useCamera / >",
I changed the anonymous function to just a function.
I tried to call useCamera() from other part of the code (not from onPress) .
I tried to use Context,and Redux.
ALL THE SAME ERROR.
please help meee,
Thanks!
I've had this problem before as well what version of React-Native are you using?
React 16.8.0 is the first release to support Hooks. When upgrading, don’t forget to update all packages, including React DOM. React Native supports Hooks since the 0.59 release of React Native.
https://reactjs.org/docs/hooks-intro.html
I see the sign, you call this.setState, it means you are using a class-based component.
a react hook can be used only in a functional component.
You have to convert the component which called LaunchCamera() (it should be renamed useCamera()) to the functional component.

how to call a react hook in a button click callback in react-admin or what to do instead?

React-admin provides hooks like useGetOne and useCreate to read and write data using rest calls.
I need to create a new record in a button callback function after collecting some data from different sources. Unfortunately it is not allowed to call react hooks from callbacks. So what can I do instead?
My problem is that I still haven't understood how REACT and final-form works. I'm from the ASP.NET corner, so I'm familiar with the concept of controls, which is obviously quite remarkably different.
The code below throws the following error at runtime:
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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app See /link removed/ for tips about how to debug and fix this problem.
The error occurs inside GetMeasurementTemplate() which uses another hook getOne() to fetch a record.
const createMeasurementJob = ( record ) => {
var counter = 0;
if (record.freimessen.messungen.length > 0) {
record.freimessen.messungen.forEach(element => {
counter++;
var job = {};
job.internalOrderNumber = "TEST" + record.id + "-" + counter;
const measurement = GetMeasurementTemplate(element.ort);
job.locationId = measurement.location;
job.substanceIds = measurement.substances;
job.comment = element.kommentar;
job.assigneeId = element.gaspruefer;
job.date = record.arbeit.ab;
// #### error: invalid hook call ####
const [create, { loading, error }] = useCreate('jobs', job); //* BANG! *//
if (error) { return false }
})
}
return true;
};
const GetMeasurementTemplate = (id) => {
// #### error: invalid hook call ####
const { data, loading, error } = useGetOne('LocationSubstanceMapping', id);
if (loading) { return <LinearProgress />; }
if (error) { return <p>ERROR</p>; }
return data;
};
const CSEButton = ({ handleSubmitWithRedirect, ...props }) => {
const form = useForm();
var formdata = form.getState().values;
const handleClick = useCallback(() => {
createMeasurementJob(formdata)
}, [form]);
return <SaveButton {...props} handleSubmitWithRedirect={handleClick} />;
};

Resources