Can't find variable error in `getDerivedStateFromProps` - reactjs

I am updating some deprecated code in a react native app, and so I replaced this:
UNSAFE_componentWillReceiveProps(nextProps) {
if(
this.props.started != nextProps.started ||
this.props.stopped != nextProps.stopped ||
this.props.durationOverride != nextProps.durationOverride ||
this.props.editable != nextProps.editable
) {
this.loadTimer(nextProps);
}
}
... with this:
static async getDerivedStateFromProps(nextProps, props) {
if(
props.started != nextProps.started ||
props.stopped != nextProps.stopped ||
props.durationOverride != nextProps.durationOverride ||
props.editable != nextProps.editable
) {
await loadTimer(nextProps);
}
return null;
}
This works as expected, functionally. However, I am getting this yellow text message in the console:
[Unhandled promise rejection: ReferenceError: Can't find variable:
loadTimer]
What is the issue here? Is it that I'm calling a function within getDerivedStateFromProps()? Or is the issue that it's a promise? Or is the issue to do with the loadTimer() function itself? That function looks like this:
async loadTimer(props) {
await Promise.all([
new Promise((resolve) => {
if(props.editable !== undefined) this.setState({
editable: props.editable,
}, resolve);
}),
new Promise((resolve) => {
if(props.stopped) {
this.setState({
stopTime: moment(props.stopped).format('YYYY-MM-DD HH:mm:ss'),
stopDuration: moment(props.stopped).format('HH:mm'),
}, resolve);
} else {
this.setState({
stopTime: null,
stopDuration: null,
}, resolve);
}
}),
new Promise((resolve) => {
if(props.stopTimeOffset && props.stopTimeOffset != Number.NaN) {
this.setState({ stopTimeOffset: props.stopTimeOffset }, resolve);
} else {
this.setState({ stopTimeOffset: 0 }, resolve);
}
}),
new Promise((resolve) => {
if(props.started) {
this.setState({
startTime: moment(props.started).format('YYYY-MM-DD HH:mm:ss'),
startDuration: moment(props.started).format('HH:mm'),
}, resolve);
} else {
this.setState({
startTime: null,
startDuration: null,
}, resolve);
}
})
]);
if(!props.stopped && props.started && this.state.editable) {
await this._startTimer();
} else {
if(props.durationOverride && !this.state.editable) {
this.setState({
duration: `${props.durationOverride}:00`,
durationShort: props.durationOverride,
});
} else {
const duration = this._getDuration();
this.setState({
duration: duration.long,
shortDuration: duration.short,
});
}
}
}

This isn't the kind of case that getDerivedStateFromProps is designed for. I recommend you put your code in componentDidUpdate instead:
async componentDidUpdate(prevProps) {
if(
this.props.started != prevProps.started ||
this.props.stopped != prevProps.stopped ||
this.props.durationOverride != prevProps.durationOverride ||
this.props.editable != prevProps.editable
) {
await this.loadTimer(this.props);
}
}
You may find the following article from the react team useful: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Related

React native, multiple records getting pushed after 2nd object being pushed onto the array

React Native, Im trying to check the whether the object is present in the array or not, if present Im replacing the object with the new one, but after the 2nd object is inserted 3rd object is pushed twice, 4th object 4 times, Im not understanding why this behaviour is happening, is it because of immutable array or what?
useEffect(() => {
if (route.params && route.params.lookup_scan) {
showShipments();
}
}, [route]);
const showShipments = () => {
if (SegregationContext.shipments.length > 0) {
SegregationContext.shipments.forEach((item, key: number) => {
if (item.lookup_scan === route.params.lookup_scan) {
SegregationContext.shipments[key] = route.params;
} else {
SegregationContext.shipments.push({ ...route.params });
}
});
} else {
SegregationContext.shipments.push(route.params);
}
setShipments([...SegregationContext.shipments]);
setIsLoader(false);
};
what the solution was:
const index = shipments.findIndex(i => i.lookup_scan === newobj.lookup_scan); // Returns 2.
if (index === -1) {
shipments.push(newobj);
} else {
shipments[index] = newobj;
}
This is what I did but it didnt seem to work bcz it added the object once again after finishing the loop:
if (shipments.length > 0) {
shipments.map((item, key: number) => {
if (item.lookup_scan === newobj.lookup_scan) {
shipments[key] = newobj;
}
});
shipments.push(newobj);
} else {
shipments.push(newobj);
}
This is what I was doing:
if (shipments.length > 0) {
shipments.map((item, key: number) => {
if (item.lookup_scan === newobj.lookup_scan) {
shipments[key] = newobj;
} else {
shipments.push(newobj);
}
});
} else {
shipments.push(newobj);
}
actual code:
if (SegregationContext.shipments.length > 0) {
SegregationContext.shipments.forEach((item, key: number) => {
if (item.lookup_scan === route.params.lookup_scan) {
SegregationContext.shipments[key] = route.params;
}
});
SegregationContext.shipments.push({ ...route.params });
} else {
SegregationContext.shipments.push(route.params);
}
setShipments([...SegregationContext.shipments]);

Websocket failed when first time rendering - React

new WebSocket() is failing when I firest render the application, but when I refresh the page it reconnects and works properly
error -> WebSocket connection to 'ws://localhost:3001/?editorId=a3tq1&testId=noTestIdGiven' failed:
Adding this for more information https://github.com/sanity-io/sanity/blob/next/packages/%40sanity/portable-text-editor/test/ws-server/index.ts
const webSocket = useMemo(() => {
console.log(editorId, incomingPatches$, testId)
const socket = new WebSocket(
`{ws://${window.location.hostname}:3001/?editorId=${editorId}&testId=${testId}`
);
socket.addEventListener('open', () => {
socket.send(JSON.stringify({type: 'hello', editorId, testId}))
})
socket.addEventListener('message', (message) => {
if (message.data && typeof message.data === 'string') {
const data = JSON.parse(message.data)
if (data.testId === testId) {
switch (data.type) {
case 'value':
setValue(data.value)
setRevId(data.revId)
onChange(data.value)
break
case 'selection':
if (data.editorId === editorId && data.testId === testId) {
setSelection(data.selection)
}
break
case 'mutation':
if (data.editorId !== editorId && data.testId === testId) {
data.patches.map((patch: Patch) => incomingPatches$.next(patch))
}
break
default:
// Nothing
}
}
}
})
return socket }, [editorId, incomingPatches$, testId])
const handleMutation = useCallback(
(patches: Patch[]) => {
console.log('webSocket >>', webSocket)
if (webSocket) {
webSocket.send(JSON.stringify({type: 'mutation', patches, editorId, testId}))
}
},
[editorId, testId, webSocket] )

React Native - SetTimeout() is not working. t.apply is not a function

I'm trying to play back audio after a delay of time
_onPlayPausePressed = () => {
if (this.sound != null) {
if (this.state.isPlaying) {
this.sound.pauseAsync();
} else {
setTimeout(this.sound.playAsync(), 2000);
}
}
};
However it returns error: t.apply is not a function. (In't.apply(void 0,o)','t.apply' is undefined)
I tried the updated method of Oleg. It worked the first time but it won't run again after that. Here are more insight of my code:
//import the array with sound details:{id, name desc, sound}
import { soundArray } from "./CreateRecord";
...
export default class CurrentRecord extends React.Component {
constructor(props) {
super(props);
this.currentSound = [];
this.recording = null;
this.sound = null;
this.isSeeking = false;
this.shouldPlayAtEndOfSeek = false;
this.state = {
haveRecordingPermissions: false,
isLoading: false,
isPlaybackAllowed: false,
muted: false,
soundPosition: null,
soundDuration: null,
recordingDuration: null,
shouldPlay: false,
isPlaying: false,
isRecording: false,
fontLoaded: false,
shouldCorrectPitch: true,
volume: 1.0,
rate: 1.0,
isModalVisible: false
};
this.recordingSettings = JSON.parse(
JSON.stringify(Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY)
);
}
//load the audio when the component mount
componentDidMount() {
this.loadAudio();
(async () => {
await Font.loadAsync({
"cutive-mono-regular": require("../../assets/fonts/CutiveMono-Regular.ttf")
});
this.setState({ fontLoaded: true });
})();
this._askForPermissions();
}
//load the sound
async loadAudio() {
const { navigation } = this.props;
const id = navigation.getParam("id");
this.sound = new Audio.Sound();
for (let i = 0; i < soundArray.length; i++) {
if (soundArray[i].id === id) {
this.currentSound = soundArray[i];
console.log(this.currentSound);
break;
}
}
try {
await this.sound.loadAsync({
uri: this.currentSound.sound /* url for your audio file */
});
await this.sound.setOnPlaybackStatusUpdate(
this._updateScreenForSoundStatus
);
} catch (e) {
console.log("ERROR Loading Audio", e);
}
}
//change screen based on the status
_updateScreenForSoundStatus = status => {
if (status.isLoaded) {
this.setState({
soundDuration: status.durationMillis,
soundPosition: status.positionMillis,
shouldPlay: status.shouldPlay,
isPlaying: status.isPlaying,
rate: status.rate,
muted: status.isMuted,
volume: status.volume,
shouldCorrectPitch: status.shouldCorrectPitch,
isPlaybackAllowed: true
});
} else {
this.setState({
soundDuration: null,
soundPosition: null,
isPlaybackAllowed: false
});
if (status.error) {
console.log(`FATAL PLAYER ERROR: ${status.error}`);
}
}
};
_askForPermissions = async () => {
const response = await Permissions.askAsync(Permissions.AUDIO_RECORDING);
this.setState({
haveRecordingPermissions: response.status === "granted"
});
};
//here's where i want to delay the audio file.
_onPlayPausePressed = () => {
if (this.sound != null) {
if (this.state.isPlaying) {
this.sound.pauseAsync();
} else {
setTimeout(() => this.sound.playAsync(), 2000);
}
}
};
_onStopPressed = () => {
if (this.sound != null) {
this.sound.stopAsync();
}
};
_onMutePressed = () => {
if (this.sound != null) {
this.sound.setIsMutedAsync(!this.state.muted);
}
};
_onVolumeSliderValueChange = value => {
if (this.sound != null) {
this.sound.setVolumeAsync(value);
}
};
_trySetRate = async (rate, shouldCorrectPitch) => {
if (this.sound != null) {
try {
await this.sound.setRateAsync(rate, shouldCorrectPitch);
} catch (error) {
// Rate changing could not be performed, possibly because the client's Android API is too old.
}
}
};
_onRateSliderSlidingComplete = async value => {
this._trySetRate(value * RATE_SCALE, this.state.shouldCorrectPitch);
};
_onPitchCorrectionPressed = async value => {
this._trySetRate(this.state.rate, !this.state.shouldCorrectPitch);
};
_onSeekSliderValueChange = value => {
if (this.sound != null && !this.isSeeking) {
this.isSeeking = true;
this.shouldPlayAtEndOfSeek = this.state.shouldPlay;
this.sound.pauseAsync();
}
};
_onSeekSliderSlidingComplete = async value => {
if (this.sound != null) {
this.isSeeking = false;
const seekPosition = value * this.state.soundDuration;
if (this.shouldPlayAtEndOfSeek) {
this.sound.playFromPositionAsync(seekPosition);
} else {
this.sound.setPositionAsync(seekPosition);
}
}
};
_getSeekSliderPosition() {
if (
this.sound != null &&
this.state.soundPosition != null &&
this.state.soundDuration != null
) {
return this.state.soundPosition / this.state.soundDuration;
}
return 0;
}
_getMMSSFromMillis(millis) {
const totalSeconds = millis / 1000;
const seconds = Math.floor(totalSeconds % 60);
const minutes = Math.floor(totalSeconds / 60);
const padWithZero = number => {
const string = number.toString();
if (number < 10) {
return "0" + string;
}
return string;
};
return padWithZero(minutes) + ":" + padWithZero(seconds);
}
_getPlaybackTimestamp() {
if (
this.sound != null &&
this.state.soundPosition != null &&
this.state.soundDuration != null
) {
return `${this._getMMSSFromMillis(
this.state.soundPosition
)} / ${this._getMMSSFromMillis(this.state.soundDuration)}`;
}
return "";
}
_getRecordingTimestamp() {
if (this.state.recordingDuration != null) {
return `${this._getMMSSFromMillis(this.state.recordingDuration)}`;
}
return `${this._getMMSSFromMillis(0)}`;
}
Change to
_onPlayPausePressed = () => {
if (this.sound != null) {
if (this.state.isPlaying) {
this.sound.pauseAsync();
} else {
setTimeout(()=>this.sound.playAsync(), 2000);
}
}
};
Remove execution of function. Also use arrow function(()=> {}) in playAsync
_onPlayPausePressed = async () => {
if (this.sound != null) {
if (this.state.isPlaying) {
await this.sound.pauseAsync();
} else {
setTimeout(() => {
await this.sound.playAsync()
}, 2000);
}
}
};

Why is not the state updated?

I have a function that updates a state with a change and adds a value, but the state in the 'addResponse' function does not always change:
handleSelected (e, item) {
this.setState({
current_component_id: item.id,
}, () => this.addResponse()
);
};
Call function above:
addResponse (e) {
const { enrollment_id, evaluation_id, user_id, question_id, current_component_id,
responses, current_question, current_question_id
} = this.state;
console.log(current_component_id)
if (current_component_id != 0) {
const newResponse = {
enrollment_id: enrollment_id,
evaluation_id: evaluation_id,
user_id: user_id,
question_id: current_question_id,
answer_component: current_component_id,
};
function hasAnswer(res) {
const list_question_id = res.map((item) => {
return item.question_id
});
if (list_question_id.includes(current_question_id)) {
return true
} else {
return false
}
}
if (responses === undefined) {
this.setState({
responses: [newResponse]
}
, () => console.log('---------> primeiro', this.state.responses)
)
} else {
const check = hasAnswer(responses);
if (check) {
this.setState(prevState => {
prevState.responses.map((item, j) => {
if (item.question_id === current_question_id) {
return item.answer_component = current_component_id
}
return item ;
})
}
, () => { console.log('----> questao alterada ', this.state.responses)}
)
} else {
this.setState({
responses: [...this.state.responses, newResponse]
}
, () => console.log('------> questao nova', this.state.responses)
);
}
}
}
// this.nextQuestion();
};
the first console.log is always correct, but the others do not always change, I know that setState is asyn, but I thought that as I call the addResponse function it would be async
There is a problem in your how you call setState when check is true.
It should be
this.setState(prevState => ({
responses: prevState.responses.map((item, j) => {
if (item.question_id === current_question_id) {
item.answer_component = current_component_id
}
return item ;
})
})
, () => { console.log('----> questao alterada ', this.state.responses)}
)

React Native function not triggering

So, I have this component,
{ this.props.isConfirmModalOpen && shiftInvite && <ConfirmApplicationPopUp
memberPhoto={props.memberProfile && props.memberProfile.photo}
venueLogo={getOr('noop', 'account.logo', shiftInvite)}
isOpen={props.isConfirmModalOpen}
shift={shiftInvite}
isOnboarding={isOnboardingMember}
onClose={props.onToggleConfirmPopUp}
onConfirm={this.handleConfirmApplication}
checkValues={props.confirmationCheckValues}
onUpdateCheckValues={props.onUpdateConfirmationCheckValues}
isHomeComponent
/> }
As you can see, I pass on onConfirm the handleConfirmApplication function, which is a check for some stuff and has to run a function in the try block, , here's the function
handleConfirmApplication = async () => {
const checkVals =
get('shift.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
console.log('inTry1');
await this.handleShiftInviteDecision('ACCEPT');
console.log('inTry2');
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
console.log('inFinally');
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
My problem is, for whatever reason, it doesn't run the handleShiftInviteDecision('ACCEPT) for whatever reason, i'm awaiting it, tried to put it in another function, call them both from another function ETC, the function does not run!
Here's the handleShiftInviteDecision function too
handleShiftInviteDecision = (decision: 'ACCEPT' | 'DECLINE') => async () => {
console.log('handleSIDecision1');
const [shiftInvite] = getOr([], 'shiftInvites', this.state.modals);
console.log('handleSIDecision2');
if (decision === 'ACCEPT') {
analytics.hit(new PageHit(`ShiftInviteModal-ACCEPT-${shiftInvite.id}`));
console.log('handleSIDecision3');
} else if (decision === 'DECLINE') {
analytics.hit(new PageHit(`ShiftInviteModal-DECLINE-${shiftInvite.id}`));
console.log('handleSIDecision4');
}
try {
console.log("thisSHouldRun")
this.setState({ isLoading: true, display: false });
await this.props.updateMyApplication(shiftInvite.id, decision);
console.log('handleSIDecision5');
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.setState({ isLoading: false, display: false });
}
};
Any ideeas on what I could do?
The function handleShiftInviteDecision is a function that returns an async function.
handleShiftInviteDecision = (decision: 'ACCEPT' | 'DECLINE') => async () => {
So, you would need to call the async function it returns to invoke it:
try {
console.log('inTry1');
await this.handleShiftInviteDecision('ACCEPT')(); // <--- here
console.log('inTry2');
} catch (e) {

Resources