React state not updating immediately - reactjs

The React state is not updating immediately. I want to update the state immediately on the press of Play button.
import * as React from "react";
import { Button } from "react-native";
export default function Play() {
const [player, setPlayer] = React.useState(1);
function nextPlayer() {
setPlayer(2);
updateValue();
}
function updateValue() {
if (player == 1) {
console.log("player 1");
} else if (player == 2) {
console.log("player 2");
}
}
return <Button title="play" onPress={nextPlayer} />;
}

The function updateValue is created in the Closure that contains the "old value" of your state. If you want it to run with the new values, use an useEffect hook or pass the current value as an argument.
const [value, setValue] = useState();
useEffect(() => {
// receives the latest value and is called on every change (also the first one!)
}, [value])

Related

How to fix invalid hook call error in my airtable extension?

I'm getting this error in my Airtable blocks extension:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
I'm unsure as to why. My program uses airtable blocks api to create a dropdown that sets a useState value, before useEffect updates a database live. Both hooks are being used inside the function body, and the function should be a react component, so I don't understand where the error lies. I've seen it can be due to react version conflicts and such as well, but I'm not sure how to confirm whether or not that is the underlying issue.
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import {
Select,
initializeBlock,
SelectSynced,
useBase,
useRecords,
BaseProvider,
useGlobalConfig,
expandRecord,
TablePickerSynced,
ViewPickerSynced,
FieldPickerSynced,
FormField,
Input,
Button,
Box,
Icon,
} from '#airtable/blocks/ui';
import { FieldType } from '#airtable/blocks/models';
const base = useBase();
const table = base.getTable("National Works In Progress");
export default function FilterApp() {
// YOUR CODE GOES HERE
let records = useRecords(table);
var aunumbers_array = [];
const [value, setValue] = useState("");
const queryResult = table.selectRecords({ fields: ["AU Number"] });
records.forEach(function (x) {
if (aunumbers_array.indexOf(x.getCellValueAsString("AU Number"), -1)) {
aunumbers_array.push({ value: x.getCellValueAsString("AU Number"), label: x.getCellValueAsString("AU Number") })
}
});
queryResult.unloadData();
let updates = [];
useEffect(() => {
const fetchData = async () => {
records.forEach(function (x) {
if (x.getCellValueAsString('AU Number') == value) {
updates.push({ id: x.id, fields: { 'Matches Filter': true } });
console.log(value);
}
else if (x.getCellValueAsString('AU Number') !== value && x.getCellValueAsString('Matches Filter') == 'checked') {
updates.push({ id: x.id, fields: { 'Matches Filter': false } });
}
});
while (updates.length) {
await table.updateRecordsAsync(updates.splice(0, 50));
}
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);
}, [value])
return (
<div>
<FormField label="Text field">
<Select
options={aunumbers_array}
value={value}
onChange={newValue => setValue(newValue.toString())}
width="320px"
/>
</FormField>
</div>
);
}
The error is because you are calling useBase, a custom hook in an invalid way.
const base = useBase();
This is wrong way of calling a hook as hooks can only be called inside of the body of a function component.
Move the hook call inside FilterApp() functional component.

how to register onClick and onDoubleClick event from same JSX element in React Functional Component?

Is there a proper , established way to register either User permormed single click or double click on same JSX element inside of Function component. After reading articles on stackOverflow and watching youtube, the easiest solution for beginner like me was to create custom hook - useClickHook, and to use callback inside setTimeout api. In App component I'm using useEffect hook,
clickHook value is inside of array of dependencies. On first render its 0 , after first click = 1 , if doubleClick = 2; inside of If() statement in useeffect - console.log() reperesents function to be invoked. and after i'm setting clickHook value back to default 0.
Here is what I coded (minimal reproducible example)
import { useState, useEffect } from 'react';
export const useClickHook = (detail) => {
const [clickDetail, setClickDetail] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setClickDetail(detail)
}, 200);
return () => {
clearTimeout(timer);
}
}, [detail]);
return clickDetail;
}
import { useEffect, useState } from 'react';
import { useClickHook } from './ClickHook';
function App() {
const [click, setClick] = useState(0);
const clickHook = useClickHook(click);
useEffect(() => {
if (clickHook === 1) {
console.log('single click')
}
if (clickHook === 2) {
console.log('double click')
}
setClick(0);
},[clickHook])
return (
<div className="App">
Hello World
<button
onClick={(e) => {
setClick(e.detail);
}}
onDoubleClick={(e) => {
setClick(e.detail);
}}
>
Click
</button>
</div>
);
}
export default App;
How to improve it?
Will be glad for every suggestions.
Edited!!!
So one way to improve is simple to remove onDoubleClick event handler from button JSX element .
Following code can be safely deleted
onDoubleClick={(e) => {
setClick(e.detail);
}}
Thus , on double click setClick will be called only twice , not 3 times like in example proposed originaly , and the rest will be done by the custom hook as before.
Pretty sure you can already do this in React with no special code required.
Demo here
export default function Demo() {
const handleClick = (event) => {
console.log(event.detail);
switch (event.detail) {
case 1: {
console.log("single click");
break;
}
case 2: {
console.log("double click");
break;
}
default: {
break;
}
}
};
return (
<div>
<div>
<button onClick={handleClick}>Double click</button>
</div>
</div>
);
}

Updating state without rendering the whole React component (useState)

I have a component that instantiates a few classes from the Tone.js library (e.g audio players and filters) and defines a few functions acting on these objects, which are used as callbacks in a set of UI-rendered buttons (see relevant code below).
Two of these buttons are supposed to toggle the boolean state is3D using the useState hook (in the updateSpatial function) in order to enable/disable one of these buttons. However, this update obviously causes the component to re-render entirely, thus re-instantiating my classes, which prevent the defined functions to work afterwards.
In contrast, I also tried the useRef hook, which allows for is3D update without re-rendering, but the button's disabled state is not updated as the component does not re-render.
Is there a pattern that fits with this situation? My next attempts include using HOC, Context, or Redux, but I'm not sure this is the most straighforward. Thanks!
import React, {useState, useEffect, Fragment} from 'react'
import { Player, Destination} from "tone";
const Eagles = () => {
const [is3D, setIs3D] = useState(false);
useEffect(() => {
// connect players to destination, ready to play
connectBase();
});
// Instantiating players
const player1= new Player({
"url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguabeber.m4a",
"autostart": false,
"onload": console.log("player 1 ready")
});
const player2 = new Player({
"url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguas.m4a",
"autostart": false,
"onload": console.log("player 2 ready")
});
// Functions
function connectBase() {
player1.disconnect();
player1.connect(Destination);
player2.disconnect();
player2.chain(Destination);
}
function playStudio() {
player1.volume.value = 0;
player2.volume.value = -60;
}
function playCinema() {
player1.volume.value = -60;
player2.volume.value = 0;
}
function playstereo() {
player1.volume.value = 0;
player2.volume.value = -60;
updateSpatial();
}
function playmyhifi() {
player1.volume.value = -60;
player2.volume.value = 0;
updateSpatial();
}
function start() {
player1.start();
player2.start();
playstereo();
}
function stop() {
player1.stop();
player2.stop();
console.log("stop pressed")
}
// Update state to toggle button enabled`
function updateSpatial() {
setIs3D((is3D) => !is3D);
}
return (
<Fragment>
<ButtonTL onClick={start}>Play</ButtonTL>
<ButtonTR onClick={stop}>Stop</ButtonTR>
<ButtonTL2 onClick={playstereo}>Stereo</ButtonTL2>
<ButtonTR2 onClick={playmyhifi}>3D</ButtonTR2>
<ButtonLL disabled={is3D} onClick={playStudio}>Studio</ButtonLL>
<ButtonLR onClick={playCinema}>Cinema</ButtonLR>
</Fragment>
)
}
I see two things wrong with your code.
First, everything within the "normal" body of the function will be executed on on every render. Hence, the need for states and hooks.
States allow you to keep data between renders, and hooks allow you to do a particular action upon a state change.
Second, useEffect(()=>console.log(hi)) does not have any dependencies, hence it will run on every render.
useEffect(()=>console.log(hi),[]) will execute only on the first render.
useEffect(()=>console.log(hi),[player1]) will execute when player1 changes.
useEffect(()=>console.log(hi),[player1, player2]) will execute when player1 OR player2 change.
Be careful with hooks with dependencies. If you set the state of one of the dependencies within the hook itself, it will create an infinite loop
Here is something closer to what you want:
import React, {useState, useEffect, Fragment} from 'react'
import { Player, Destination} from "tone";
const Eagles = () => {
const [is3D, setIs3D] = useState(false);
const [player1]= useState(new Player({
"url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguabeber.m4a",
"autostart": false,
"onload": console.log("player 1 ready")
}));
const [player2] = useState(new Player({
"url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguas.m4a",
"autostart": false,
"onload": console.log("player 2 ready")
}));
useEffect(() => {
// connect players to destination, ready to play
connectBase();
},[player1, player2]);
// Instantiating players
// Functions
function connectBase() {
player1.disconnect();
player1.connect(Destination);
player2.disconnect();
player2.chain(Destination);
}
function playStudio() {
player1.volume.value = 0;
player2.volume.value = -60;
}
function playCinema() {
player1.volume.value = -60;
player2.volume.value = 0;
}
function playstereo() {
player1.volume.value = 0;
player2.volume.value = -60;
updateSpatial();
}
function playmyhifi() {
player1.volume.value = -60;
player2.volume.value = 0;
updateSpatial();
}
function start() {
player1.start();
player2.start();
playstereo();
}
function stop() {
player1.stop();
player2.stop();
console.log("stop pressed")
}
// Update state to toggle button enabled`
function updateSpatial() {
setIs3D((is3D) => !is3D);
}
return (
<Fragment>
<ButtonTL onClick={start}>Play</ButtonTL>
<ButtonTR onClick={stop}>Stop</ButtonTR>
<ButtonTL2 onClick={playstereo}>Stereo</ButtonTL2>
<ButtonTR2 onClick={playmyhifi}>3D</ButtonTR2>
<ButtonLL disabled={is3D} onClick={playStudio}>Studio</ButtonLL>
<ButtonLR onClick={playCinema}>Cinema</ButtonLR>
</Fragment>
)
}

How to check my conditions on react hooks

I need check my conditions on react hooks when startup or change value
I try that by this code but I cant run without Btn
import React,{useEffect} from 'react';
import { StyleSheet, View } from 'react-native';
import * as Permissions from 'expo-permissions';
import { Notifications} from 'expo';
export default function NotificationsTest() {
const y = 5;
const askPermissionsAsync = async () => {
await Permissions.askAsync(Permissions.USER_FACING_NOTIFICATIONS);
};
const btnSendNotClicked = async () => {
if(y===5){
await askPermissionsAsync();
Notifications.presentLocalNotificationAsync({
title: "Title",
body: "****** SUBJ *******",
ios:{
sound:true,
},
android:{
sound:true,
color:'#512da8',
vibrate:true,
}
});
}else{
} }
useEffect(()=>{
return btnSendNotClicked
}
,[])
return (
<View style={styles.container}>
</View>
);
}
and I just want to confirm is it good practice to checking this kind of condition in useEffect ?
Hi on startup useEffect with empty array will be fired and this will be happened during the page load (first time), so you can write your condition there. here is an example:
useEffect(()=> {
// Your condition here
}, []);
If you have a variable like value then you can write another useEffect like below and set that variable (value) in second parameter of useEffect as an array
const [value, setValue] = useState('');
useEffect(()=> {
// Your condition here
}, [value]);
const y = 5;
//this one will run every time the component renders
//so you could use it to check for anything on the startup
useEffect(()=> {
// Your condition here
}, []);
Otherwise you could add a value or array of values that you want to track for changes so this useEffect bellow Will only run if the y const changes so you could add an if check there to check if y===5 also this useEffect will run on the first initial of the const y so its perfect in your case
}
useEffect(()=> {
// will run every time y const change and on the first initial of y
if (y === 5)
{
//do your stuff here
}
}, [y]);

React useState - setValue rerenders the component even though the value is the same

I'm using useState hook in a custom hook.
I'm calling the setValue function that is returned from the useState twice:
1) After an onChange event
2) After the component was notified of a change from the server.
The flow of events is:
onChange event (in the component) triggers setValue = rerender
I'm using a useEffect (=after rerender) hook to update the server of the change - API function is called to update the server
I have a custom service which receives the server's response and notifies the component
When notified the component calls setValue again but in this case, the value is the same so no need to rerender.
My problem is the component gets rerendered after it was notified of the changes even though the value received is the same.
My code:
Gain Component
import * as React from 'react';
import { useDSPParamUpdate } from '../../../Hooks/useDSPParamUpdate';
import { ControlParamProps } from '../..';
const Gain = (props: ControlParamProps) => {
let min: number = 0;
let max: number = 0;
const { paramId, controlId } = props;
const { param, value, setValue } = useDSPParamUpdate({ controlId, paramId })
if (param && param.range && param.range.length >= 2) {
min = param.range[0];
max = param.range[1];
}
/*calls the setValue from the hook*/
const handleChange = (event: any) => {
const newValue = event.target.value;
setValue(newValue);
}
return (
<div className="gain">
{max}
<input className="dsp-action range-vertical" type="range"
min={min}
max={max}
value={value}
onChange={handleChange} />
{min}
</div>
);
}
export default Gain;
useDSPParamUpdate - custom hook
import * as React from 'react';
import { ControlParamProps } from '../dsp';
import { dspService } from '../../core/services/dsp.service';
export function useDSPParamUpdate(props: ControlParamProps) {
const initValue = ...
const [value, setValue] = React.useState(initValue);
function updateDevice() {
// calls some API func to update the server (sends the value)
}
// subscribes to server changes
React.useEffect(() => {
// subscribrs to server notifications
let unsubscribe = dspService.subscribe((newVal) => setValue(newVal));
return () => {
unsubscribe();
};
}, []);
// sends data to the server after update
React.useEffect(() => {
updateDevice();
}, [value]);
return { param, value, setValue };
}
Typically it's not an issue if render() is called extra time.
But if you want you may guard calling setValue() by checking if value is the same
let unsubscribe = dspService.subscribe(
(newVal) =>
newVal !== value && setValue(newVal)
);
Maybe slightly verboose way but it's the same approach as typically used in componentDidUpdate
Note that useState does not provide any logic like shouldComponentUpdate has. So if you want to make it in more declarative way you have to refactor your component to be class-based accessor of PureComponent.

Resources