How to solve: Unable to find node on an unmounted component - reactjs

I am facing this this error: Unable to find node on an unmounted component in my code.
This is my set of code:
class CalendarScheduler extends Component {
state = {
viewModel: schedulerData,
showBookingDialog: false
}
handleClickOpen = () => {
// this.setState({ showBookingDialog: true });
this.setState((prevState) => {
return { showBookingDialog: !prevState.showBookingDialog };
})
};
handleClose = () => {
this.setState({ showBookingDialog: false });
};
render() {
const { viewModel } = this.state;
let schedulerData = this.state.viewModel;
schedulerData.setResources(this.props.rooms);
return (
<div>
<Scheduler schedulerData={viewModel}
prevClick={this.prevClick}
nextClick={this.nextClick}
onSelectDate={this.onSelectDate}
newEvent={this.newEvent}
/>
{this.state.showBookingDialog === true && (
<BookingDialog
open={this.state.showBookingDialog}
onClose={this.handleClose} />
)}
</div>
);
}
This is my function:
newEvent = (schedulerData, slotId, slotName, start, end, type, item) => {
this.setState({
showBookingDialog: true
});
My error message is pointing to this.setState inside newEvent.

Related

React class component not updating the state on Router

class App extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
userWorkSpecialIds: false,
};
}
isAuthorized = (user) => {
const { user } = this.state;
if (!user) return false;
else {
// check to see if user has special work permit id
this.hasWorkAccount(user);
return user.some((item) => user.email === item.id);
}
}
isLoaded = () => {
const { user } = this.state;
return user != null;
}
hasWorkAccount = () => {
getCustomerList(function(data) { // service gets users work place from firebase
const checkworkerSpecialIds = data.map((item) => {
return {
id: item.w_id,
name: item.w_name,
special_id: item.wspecial_id,
};
});
// checkworkerSpecialIds returns the user list of Ids correctly
console.log("checking for spacial_id : ", checkworkerSpecialIds);
// *** ERROR: ***
this.setState({ userWorkSpecialIds: !this.userWorkSpecialIds})
console.log("never reachs here when I have this.setState({}) ");
});
}
componentDidMount() {
const DataArray = [];
firebaseAppFirestore
.collection("users")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
myDataArray.push({ id: doc.id, role: doc.role });
});
this.setState({ user: DataArray });
});
}
}
render() {
return (
<UserProvider>
<FirestoreProvider firebase={firebase}>
{this.isLoaded() ? (
this.isAuthorized(firebaseAppAuth.currentUser) ? (
<Router history={browserHistory}>
<Routes
users={this.state.users}
userWorkSpecialIds={this.state.userWorkSpecialIds} <----- Not updating after 1st render
/>
</Router>
) : (
<SignInView/>
)
) : (
<CircularProgress />
)}
</FirestoreProvider>
</UserProvider>
);
}
}
export default withFirebaseAuth({
firebaseAppAuth,
})(App);
I'm stuck on *** ERROR: ***, after I retrieve the checkworkerSpecialIds, should the
this.setState({ userWorkSpecialIds: !this.userWorkSpecialIds}), trigger the component and update userWorkSpecialIds.
I want to trigger the state userWorkSpecialIds updates and update the props
Currently the Routs props just receives does userWorkSpecialIds = false, never gets updated.

React: Play, pause onclick fuctionality

I am trying to implement play/pause on button clicking. While icons change perfectly, my actual functionality fails to work. My code is the following.
Function that is triggered on click
onPlayHandler = () => {
let aud = new Audio(this.props.data[this.state.index].audio);
aud.play();
this.setState({
isPlaying: true
});
};
onPauseHandler = () => {
let aud = new Audio(this.props.data[this.state.index].audio);
aud.pause()
//aud.setAttribute("ref", `${this.myRef}`);
this.setState({
isPlaying: false
});
//this.refs.audio.pause();
//x.pause();
//console.log(aud)
};
Button that is clicked
<Button playAudio={ this.state.isPlaying ? this.onPauseHandler : this.onPlayHandler } isPlaying={ this.state.isPlaying } />
Button component
export default function IconLabelButtons(props) {
const classes = useStyles();
return (
<>
<Button
variant="contained"
color="primary"
size="large"
className={ classes.button }
startIcon={ props.isPlaying ? <PauseIcon /> : <PlayArrowIcon /> }
onClick={ props.playAudio }
>
{ props.isPlaying ? 'Pause' : 'Play' }
</Button>
</>
);
Updated question
Entire code
import React, { Component } from 'react';
import Card from './Card';
import Button from './Button';
import correct from '../../data/media/correct.wav';
import denied from '../../data/media/denied.mp3';
var _ = require('lodash');
class AudioContainer extends Component {
constructor (props) {
super(props);
this.state = {
images: [],
index: 0,
checkedItems: new Map(),
correct: false,
isPlaying: false
};
this.audio = React.createRef();
}
componentDidMount() {
const arrImages = this.props.data.map((item) => {
let arr = [];
arr.push(item.picture);
return arr;
});
//console.log(arrImages)
this.setState({
images: _.shuffle(arrImages.flat(Infinity))
});
}
showImages = () => {
this.state.images && this.state.images.map((image) => (
<div>
<figure class="image is-128x128">
<img src={ image.src } alt="" />
</figure>
<span>{ image.name }</span>
</div>
));
};
handleChange = (e) => {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(item, isChecked) }));
};
onCheckHandler = (e) => {
e.preventDefault();
/* function checkTrue(item) {
return item.value === true;
}
const filteredItems = this.state.checkedItems.filter(checkTrue) */
//console.log(this.state.checkedItems.value())
let arr = [];
// eslint-disable-next-line no-unused-vars
for (let [key, value] of this.state.checkedItems) {
let obj = {
value: key,
bool: value
};
arr.push(obj);
}
//console.log(arr);
let filteredArr = arr.filter((el) => {
return el.bool === true;
});
//console.log(filteredArr);
let arrWithAnswers = [];
filteredArr.map((el) => {
return arrWithAnswers.push(el.value);
});
//console.log(arrWithAnswers);
//console.log(this.props.data[this.state.index].answers.sort());
if (_.isEqual(arrWithAnswers.sort(), this.props.data[this.state.index].answers.sort())) {
let sound = new Audio(correct);
sound.play();
this.setState({
index: this.state.index + 1, checkedItems: new Map()
});
//console.log("true");
} else {
let sound = new Audio(denied);
sound.play();
}
};
update() {
const arrImages = this.props.data.map((item) => {
let arr = [];
arr.push(item.picture);
return arr;
});
//console.log(arrImages)
this.setState({
images: _.shuffle(arrImages.flat(Infinity))
});
}
onPlayHandler = () => {
let aud = new Audio(this.props.data[this.state.index].audio);
aud.play();
this.setState({
isPlaying: true
});
};
onPauseHandler = () => {
let aud = new Audio(this.props.data[this.state.index].audio);
aud.pause()
//aud.setAttribute("ref", `${this.myRef}`);
this.setState({
isPlaying: false
});
//this.refs.audio.pause();
//x.pause();
//console.log(aud)
};
render() {
//console.log(this.props.data)
//console.log(this.state.images)
//console.log(this.state.checkedItems.size);
return (
<div>
<Button playAudio={ this.state.isPlaying ? this.onPauseHandler : this.onPlayHandler } isPlaying={ this.state.isPlaying } />
<div className="columns is-vcentered is-multiline">
{ this.state.images && this.state.images.map((image, index) => (
<div className="column is-3" key={index}>
<Card image={ image.src } clickHandler={ this.handleChange } name={ image.name } />
</div>
)) }
</div>
<button className="button is-info" disabled={ this.state.checkedItems.size <= 0 } onClick={ this.onCheckHandler }>Check</button>
</div>
);
}
}
export default AudioContainer;
Your help is appreciated, thanks.
I was able to get it the way I want to, but I am not sure if it is a good practice. So what I did is the following.
let audio = new Audio()
class AudioContainer extends Component {
constructor (props) {
super(props);
this.state = {
images: [],
index: 0,
checkedItems: new Map(),
correct: false,
isPlaying: false,
error: false
};
}
onPlayHandler = () => {
audio.src = this.props.data[this.state.index].audio
audio.play();
this.setState({
isPlaying: true
});
};
onPauseHandler = () => {
audio.src = this.props.data[this.state.index].audio
//let aud = new Audio(this.props.data[this.state.index].audio);
audio.pause();
//aud.setAttribute("ref", `${this.myRef}`);
this.setState({
isPlaying: false
});
//this.refs.audio.pause();
//x.pause();
//console.log(aud)
};
I am very suspicious of declaring variable audio at the very top. So please correct me if I am wrong.

React this.setState function gives error when try to set new state value

When I try to update state of component with this.setState function (with or without callback) it gives following warning followed by an error as shown below.
Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback.
Uncaught TypeError: itemValue.toLowerCase is not a function
import React from 'react';
import ReactAutocomplete from 'react-autocomplete';
export default class AddMedicine extends React.Component {
constructor (props) {
super(props)
this.state = {
autocompleteData: props.list,
bid: '',
formId: '',
formsOfBrand: []
};
this.renderBrandItem = this.renderBrandItem.bind(this);
this.onChangeBrand = this.onChangeBrand.bind(this);
this.onSelectBrand = this.onSelectBrand.bind(this);
this._getFormsByBID = this._getFormsByBID.bind(this);
this.renderFormItem = this.renderFormItem.bind(this);
this.onChangeForm = this.onChangeForm.bind(this);
this.onSelectForm = this.onSelectForm.bind(this);
}
renderBrandItem(item, highlighted) {
return <div
key={item.bid}
style={{ backgroundColor: highlighted ? '#eee' : 'transparent'}}
>
{item.brand_name}
</div>
}
onChangeBrand(e) { this.setState({
bid: e.target.value,
formsOfBrand : ['dd', 'dfsd']
})
}
onSelectBrand(bid) {
this.setState( (prevState) => {
let newState = {...prevState};
newState.bid = bid;
newState.formsOfBrand = this._getFormsByBID(bid);
console.log(newState);
return newState;
});
}
componentDidUpdate() {}
_getFormsByBID(id){
let brand = this.state.autocompleteData.filter(brand => {
console.log(brand); console.log(id);
return (brand.bid == id)? true : false; }
);
brand = brand[0];
console.log(brand);
return brand.forms;
}
renderFormItem(item, highlighted) {
return <div
key={Object.keys(item)[0]}
style={{ backgroundColor: highlighted ? '#eee' : 'transparent'}}
>
{item[Object.keys(item)[0]]}
</div>
}
onChangeForm(e) { this.setState({ formId: e.target.value }) }
onSelectForm(formId) { this.setState({ formId: formId }) }
render() {
return (
<React.Fragment>
<ReactAutocomplete
items={this.state.autocompleteData}
getItemValue={item => item.bid}
renderItem={this.renderBrandItem}
value={this.state.bid}
onSelect={this.onSelectBrand}
/>
<select>
{this.state.formsOfBrand.forEach((form) =>
<option value={Object.keys(form)[0]}>{form[Object.keys(form)[0]]}</option>
)}
</select>
</React.Fragment>
)
}
}

How to test call back refs with Enzyme and Jest

I want to test addEventListener/RemoveEventListener for an Element.
class Image extends PureComponent {
state = {
isLoaded: false,
};
componentDidMount() {
if (!this.props.src) return;
this.imageToLoad = document.createElement('img');
this.imageToLoad.addEventListener('load', this.loadImage);
this.imageToLoad.src = this.props.src;
}
componentWillUnmount() {
if (!this.imageToLoad) return;
this.imageToLoad.removeEventListener('load', this.loadImage);
}
loadImage = () => {
this.setState({ isLoaded: true }, () => {
this.myimg.src = this.imageToLoad.src;
});
}
render() {
const { className, src, alt } = this.props;
const { isLoaded } = this.state;
if (isLoaded === false || !src) return '';
return (
<img
className={className}
ref={img => (this.myimg = img)}
src={src}
alt={alt}
/>
);
}
}
Test code:-
it('should add event listener on mount', () => {
const elementMock = { addEventListener: jest.fn() };
jest.spyOn(Image.prototype, 'myimg').mockImplementation(() => elementMock);
expect(elementMock.addEventListener).toBeCalledWith('load', expect.any(Function), false);
});
But it is not working as expected.

How to stop react component from infinite re-render

I wanna use js-cookie in my app, but the time I get the cookie my component keeps re-rendering until the browser crash.
The error I get is: setState(...): Cannot update during an existing state transition ...
I've just used the shouldComponentUpdate but it caused the click events not working.
shouldComponentUpdate(nextProps, nextState)
{
return nextState.language != this.state.language;
}
Does anybody know any other solution rather than shouldComponentUpdate to stop a component from infinite re-render?
class MainLayout extends Component {
constructor(props) {
super(props);
console.log('constructor');
this.state = {
sideBarOpen: false,
languages: getStoreLanguages,
language: Cookies.get('langCode')
}
}
componentWillMount() {
this.props.langCode();
this.props.defaultLangCode();
}
componentDidMount() {
$('.dropdown-toggle').megaMenu && $('.dropdown-toggle').megaMenu({ container: '.mmd' });
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.language != this.state.language;
}
toggleSidebar = () => {
this.setState({
sideBarOpen: !this.state.sideBarOpen,
});
}
overlayClickHandler = () => {
this.setState({
sideBarOpen: false,
});
}
handleLanguage = (langCode) => {
if (Cookies.get('langCode')) {
return Cookies.get('langCode');
} else {
Cookies.set('langCode', langCode, { expires: 7 });
return langCode;
}
}
render() {
let overlay = { display: this.state.sideBarOpen ? 'block' : 'none' };
const langCode = this.handleLanguage(this.props.params.id);
const isDefaultLang = isDefaultLanguage(langCode);
const isValidLang = isValidLanguage(langCode);
if (langCode && !isValidLang) {
this.props.router.push(`/${langCode}/error`);
}
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
return (
<div>
<Helmet>
<script type="application/ld+json">{structuredData()}</script>
</Helmet>
<TokenManager>
{(state, methods) => (
<div>
<WithHelmet {...this.props} />
<WithUserHeaderInfo {...this.props} />
<WithStoreDetail />
<WithDataLayerStoreDetail />
<header className='header__nav'>
<NavbarManagerWillHideOnEditor
sideBarOpen={this.state.sideBarOpen}
toggleSidebar={this.toggleSidebar}
languages={this.state.languages}
{...this.props}
/>
<div
className="mmm__overlay"
onClick={this.overlayClickHandler}
style={overlay}
/>
<div
className="mmm__overlay--hidenav"
onClick={this.overlayClickHandler}
style={overlay}
/>
</header>
<main>{this.props.children}</main>
<Modal modalId="modal-account" size="md">
{(closeModal) => (
<Auth
closeModal={closeModal} />
)}
</Modal>
{!this.props.location.pathname.startsWith('/checkout') && <FooterWillHideOnEditor languages={this.state.languages}/>}
</div>
)
}
</TokenManager>
</div>
);
}
}
const mapDispatchToProps = (dispatch, value) => {
const langCode = Cookies.get('langCode') || value.params.id;
const defaultLang = getDefaultLanguage();
const isDefault = isDefaultLanguage(langCode);
const isValid = isValidLanguage(langCode);
const lang = !isValid || isDefault ? defaultLang : langCode;
return {
langCode: () => dispatch({ type: 'SET_LANGUAGE', payload: lang }),
defaultLangCode: () => dispatch({ type: 'SET_DEFAULT_LANGUAGE', payload: defaultLang })
}
}
export default connect(null, mapDispatchToProps)(MainLayout);
let overlay = { display: this.state.sideBarOpen ? 'block' : 'none' };
const langCode = this.handleLanguage(this.props.params.id);
const isDefaultLang = isDefaultLanguage(langCode);
const isValidLang = isValidLanguage(langCode);
if (langCode && !isValidLang) {
this.props.router.push(`/${langCode}/error`);
}
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
Here the code is resetting the state/ or mapDispatchToProps dispatched again .that's why it's rerendering.
I found the reason why component keep re-rendering, actually it was because of the condition in:
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
which is always true and gets the path and push to the route which cause the component re-render.
Thanks

Resources