"Cannot read property 'setState' of undefined" even though it is bound - javascript

I'm trying to just test that a function is indeed invoked when a click action occurs on a link in my component. I keep receiving the error Cannot read property 'setState' of undefined. But this function from another file is bound to the constructor in the component.
The component
import { toggleEditMode } from './otherFile.js'
class PersonalInformation extends Component {
constructor(props) {
super(props);
this.state = {editMode: false}
this.toggleEditMode = toggleEditMode.bind(this);
}
render(){
const { editMode } = this.state;
return(
<div>
{!editMode &&
<div className="col-md-4 hidden-sm-down">
<a
id="editingToggleButton"
className="display-block"
role="button"
href="javascript:void(0);"
onClick={() => this.toggleEditMode()}
>
<span className="icon icon-sm dls-icon-edit" />
<span className="pad-1-l">Edit</span>
</a>
</div>
}
</div>
)
}
}
The toggleEdit method
export function toggleEditMode() {
this.setState({ editMode: !this.state.editMode })
}
The test
describe('edit', () => {
it('should switch to editMode with click', () => {
const toggleEditMode = jest.fn();
const wrapper = mount(
<PersonalInformation
toggleEditMode={toggleEditMode}
/>
);
wrapper.find('#editingToggleButton').simulate('click');
expect(toggleEditMode).toHaveBeenCalled();
});
}
When it renders the error it specifically points to this.setState within the toggleEdit function.

Here is the problem
You bind the function in the constructor, but you use it as an arrow function.
What you should do is add the bind in the arrow function or only pass the function reference.
this.toggleEditMode = toggleEditMode.bind(this);
// and
onClick={this.toggleEditMode}
OR
// remove .bind from the constructor
this.toggleEditMode = toggleEditMode
// and
onClick={() => this.toggleEditMode.bind(this)}
But I think you should use the first case, because the second case will recreate the arrow function on every render.

You can simply use reactjs hooks to change the state
here is the demo.
function PersonalInformation() {
const [editMode, changeEditMode] = useState(false);
function toggleEditMode() {
changeEditMode(!editMode);
}
return (
<div>
<span className="pad-1-l" onClick={toggleEditMode}>
{!editMode
? `Not Editable (click to change)`
: `editable(click to change)`}
</span>
</div>
);
}
https://codesandbox.io/embed/determined-benz-c5km7

Related

onClick event is sending me to the correct method but from inside that method, it is not calling the inner component and it is lost

I have below EditAddressPage component which when I click on Manual Entry button, it should send me to the getAddressField method and from there go to <AddressFields /> component. I see when onCLick happens. it is sending me to the getAddressField method but from there it is lost. I was wondering if someone can give me a second though what is missing there because anything I've tried didn't work so far.
import AddressFields from 'components/widget/AddressFields';
import "./EditAddressPage.less";
const EditAddressPage = (props) => {
const getManualAddressEntry = () => {
return (
<AddressFields
{...props}
/>
)
}
return (
<div
<div className="body-container">
<span id="manually-enter-section">
{gt.gettext('Can’t find your address? ${0}',
<Button
color="primary"
iconSide="left"
iconSrc=""
onClick={() => getManualAddressEntry()}
size="medium"
variant="flat"
id="manual-entry-button"
>
Enter it manually
</Button>
)}
</span>
</div>
)
}
export default EditAddressPage;
export default class AddressFields extends PureRenderComponent {
constructor() {
super();
this.x = true;
this.y = undefined;
this.z = undefined;
}
componentWillMount() {
// something
}
componentDidMount() {
// something
}
componentWillUnmount() {
// something
}
render() {}
}
Your getManualAddressEntry() function does not render the component, it just returns it as a value.
To render it, you can use conditional rendering like so:
<Button
color="primary"
iconSide="left"
iconSrc=""
onClick={() => getManualAddressEntry()}
size="medium"
variant="flat"
id="manual-entry-button"
>
Enter it manually
</Button>
{buttonClicked && <AddressFields/>}
buttonClicked (or whatever name you prefer) should be a state that is initially set to false, and getManualAddressEntry() should toggle it between false and true
const [manualAddressOpen, setManualAddressOpen] = useState(false)
const getManualAddressEntry = () => {
setManualAddressOpen(!manualAddressOpen)
}
Example of conditional rendering:

How to test: onClick event - Jest/ Enzyeme

I am new to unit testing and I just ran a test coverage. This is the only line missing at the moment.
How could I test this line and make sure 100 % is covered
export default class Normalize extends Component {
constructor(props) {
super(props)
this.state = {}
// this.handleChange = this.handleChange.bind(this)
}
componentDidMount() {
this.props.normalizeData(null)
}
render () {
return (
<div id='normalize-container'>
<div id='normalize-header'>
</div>
<br /><br />
<h3 className='normalized-header'>{this.props.newResponse ? 'Raw Data' : 'Normalized Data'}</h3><br/>
<div>
{this.props.newResponse ? null :
this.props.THead ?
<div>
{!this.props.datasourceCatalog ?
<div id='next-button-modal'>
{/*<button title='Proceed To Shape' className='request-button' id='next-btn-ready-modal' onClick={this.props.nextTab}><img src={nextImg}/></button>*/}
<button title='Proceed To Shape' className='request-button' id='next-btn-ready-modal' onClick={(e) => {this.props.toggleModal(); this.props.nextTab();}}>
<FontAwesome
className='fa-angle-right'
name='view-externallink-img'
size='2x'/>
</button>
<h4>Proceed To Shape</h4>
</div> :
null}
<div className='normalize-table-container'>
<div className="data-table-wrapper">
<table>
<thead dangerouslySetInnerHTML={{__html: this.props.THead}} />
<tbody dangerouslySetInnerHTML={{__html: this.props.TBody}} />
</table>
</div>
</div>
</div>
: null
}
</div>
</div>
Using React JS - jest and enzyme
Testing file :
// jest mock functions (mocks this.props.func)
const normalizeData = jest.fn();
const toggleModal = jest.fn();
const nextTab = jest.fn();
const onClick = jest.fn();
// defining this.props
const baseProps = {
normalizeData,
newResponse:{},
THead:{},
TBody:{},
datasourceCatalog:{},
toggleModal,
nextTab,
onClick,
describe(' Normalize Test', () => {
let wrapper;
let tree;
beforeEach(() => wrapper = shallow(<Normalize {...baseProps} />));
it(' Should render with all of the props', () => {
Render with all props are working - but just need to make sure how to test the line above, on click with 2 props.
Thank you
Something like this should work:
it('should call toggleModal and nextTab functions on button click', () => {
// Reset info from possible previous calls of these mock functions:
baseProps.toggleModal.mockClear();
baseProps.nextTab.mockClear();
// Remove props that would end up hiding the button
wrapper.setProps({
newResponse: null,
datasourceCatalog: null
});
// Find the button and call the onClick handler
wrapper.find('#next-btn-ready-modal').simulate('click');
// Test to make sure prop functions were called via simulating the button click
expect(baseProps.toggleModal).toHaveBeenCalled();
expect(baseProps.nextTab).toHaveBeenCalled();
})
Note: you can also split these into separate tests, one to test each call separately.

I am getting an error can not read props of undefined while using this? [duplicate]

class PlayerControls extends React.Component {
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
}
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={this.onToggleLoop}
spin={this.state.loopActive}
/>
<FontAwesome
className={shuffleClassName}
name='random'
onClick={this.onToggleShuffle}
/>
</div>
);
}
onToggleLoop(event) {
// "this is undefined??" <--- here
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
I want to update loopActive state on toggle, but this object is undefined in the handler. According to the tutorial doc, I this should refer to the component. Am I missing something?
ES6 React.Component doesn't auto bind methods to itself. You need to bind them yourself in constructor. Like this:
constructor (props){
super(props);
this.state = {
loopActive: false,
shuffleActive: false,
};
this.onToggleLoop = this.onToggleLoop.bind(this);
}
There are a couple of ways.
One is to add
this.onToggleLoop = this.onToggleLoop.bind(this); in the constructor.
Another is arrow functions
onToggleLoop = (event) => {...}.
And then there is onClick={this.onToggleLoop.bind(this)}.
Write your function this way:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
Fat Arrow Functions
the binding for the keyword this is the same outside and inside the fat arrow function. This is different than functions declared with function, which can bind this to another object upon invocation. Maintaining the this binding is very convenient for operations like mapping: this.items.map(x => this.doSomethingWith(x)).
I ran into a similar bind in a render function and ended up passing the context of this in the following way:
{someList.map(function(listItem) {
// your code
}, this)}
I've also used:
{someList.map((listItem, index) =>
<div onClick={this.someFunction.bind(this, listItem)} />
)}
in my case this was the solution = () => {}
methodName = (params) => {
//your code here with this.something
}
You should notice that this depends on how function is invoked
ie: when a function is called as a method of an object, its this is set to the object the method is called on.
this is accessible in JSX context as your component object, so you can call your desired method inline as this method.
If you just pass reference to function/method, it seems that react will invoke it as independent function.
onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined
onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object
If you are using babel, you bind 'this' using ES7 bind operator
https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding
export default class SignupPage extends React.Component {
constructor(props) {
super(props);
}
handleSubmit(e) {
e.preventDefault();
const data = {
email: this.refs.email.value,
}
}
render() {
const {errors} = this.props;
return (
<div className="view-container registrations new">
<main>
<form id="sign_up_form" onSubmit={::this.handleSubmit}>
<div className="field">
<input ref="email" id="user_email" type="email" placeholder="Email" />
</div>
<div className="field">
<input ref="password" id="user_password" type="new-password" placeholder="Password" />
</div>
<button type="submit">Sign up</button>
</form>
</main>
</div>
)
}
}
I want to give an explanation of why this is undefined:
If we use this in a function that is not an arrow function, this is bound to a global object when not in strict mode. But with strict mode, this will be undefined (https://www.w3schools.com/js/js_this.asp).
And ES6 modules are always in strict mode (javascript: use strict is unnecessary inside of modules).
You can bind this in onToggleLoop function with the instance of PlayerControls component by using bind method inside the constructor:
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
this.onToggleLoop = this.onToggleLoop.bind(this)
}
Or use the arrow function instead:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
The arrow function does not have context, so this in the arrow function will represent the object that defined the arrow function.
If you call your created method in the lifecycle methods like componentDidMount... then you can only use the this.onToggleLoop = this.onToogleLoop.bind(this) and the fat arrow function onToggleLoop = (event) => {...}.
The normal approach of the declaration of a function in the constructor wont work because the lifecycle methods are called earlier.
In my case, for a stateless component that received the ref with forwardRef, I had to do what it is said here https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd
From this (onClick doesn't have access to the equivalent of 'this')
const Com = forwardRef((props, ref) => {
return <input ref={ref} onClick={() => {console.log(ref.current} } />
})
To this (it works)
const useCombinedRefs = (...refs) => {
const targetRef = React.useRef()
useEffect(() => {
refs.forEach(ref => {
if (!ref) return
if (typeof ref === 'function') ref(targetRef.current)
else ref.current = targetRef.current
})
}, [refs])
return targetRef
}
const Com = forwardRef((props, ref) => {
const innerRef = useRef()
const combinedRef = useCombinedRefs(ref, innerRef)
return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})
You can rewrite how your onToggleLoop method is called from your render() method.
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={(event) => this.onToggleLoop(event)}
spin={this.state.loopActive}
/>
</div>
);
}
The React documentation shows this pattern in making calls to functions from expressions in attributes.

React test with enzyme TypeError: Cannot read property 'state' of undefined

I'm trying to do unit testing to a component using enzyme shallow rendering. Trying to test state activeTab of the component and it throws TypeError: Cannot read property state. my component Accordion. Accordion component jsx code
class Accordion extends Component {
constructor(props) {
super(props)
this.state = {
activeTab: 0
}
}
static defaultProps = {
tabs: [{title: 'Status'}, {title: 'Movement'}]
}
render() {
const { tabs } = this.props
, { activeTab } = this.state
return (
<div className={`accordion`}>
{tabs.map((t, i) => {
const activeClass = activeTab === i ? `accordion--tab__active` : ''
return(
<section key={i} className={`accordion--tab ${activeClass}`}>
<header className={`accordion--header`}>
<h4 className={`accordion--title`}>
<button onClick={() => {this._selectAccordion(i)}}>{t.title}</button>
</h4>
</header>
<div className="accordion--content">
{t.title}
Content
</div>
</section>
)
})}
</div>
)
}
_selectAccordion = activeTab => {this.setState({activeTab})}
}
export default Accordion
and Accordion.react.test.js
import { shallow } from 'enzyme'
import Accordion from './components/Accordion'
test('Accordion component', () => {
const component = shallow(<Accordion name={`Main`}/>)
expect(component.state('activeTab')).equals(0)
})
This could be a this scoping issue. With event handlers in React, you have to bind the event handler in the constructor to "this". Here is some info from React's docs about it:
You have to be careful about the meaning of this in JSX callbacks. In
JavaScript, class methods are not bound by default. If you forget to
bind this.handleClick and pass it to onClick, this will be undefined
when the function is actually called.
This is not React-specific behavior; it is a part of how functions
work in JavaScript. Generally, if you refer to a method without ()
after it, such as onClick={this.handleClick}, you should bind that
method.
class Accordion extends Component {
constructor(props) {
super(props)
this.state = {
activeTab: 0
}
// This binding is necessary to make `this` work in the callback
this._selectAccordion = this._selectAccordion.bind(this);
}
static defaultProps = {
tabs: [{title: 'Status'}, {title: 'Movement'}]
}
_selectAccordion(activeTab){
this.setState({activeTab : activeTab})
}
render() {
const { tabs } = this.props,
{ activeTab } = this.state
return (
<div className={`accordion`}>
{tabs.map((t, i) => {
const activeClass = activeTab === i ? `accordion--tab__active` : ''
return(
<section key={i} className={`accordion--tab ${activeClass}`}>
<header className={`accordion--header`}>
<h4 className={`accordion--title`}>
<button onClick={() => {this._selectAccordion(i)}}>{t.title}</button>
</h4>
</header>
<div className="accordion--content">
{t.title}
Content
</div>
</section>
)
})}
</div>
)
}
}
Your tests should verify how the component works but not "how to change a state". You need to throw new props into your component and get a result, and the result is expected.
I've tested my components with snapshots
This is an example of my current project
describe('<Component />', () => {
it('Page rendered', () => {
const rendered = renderComponent({
...testProps,
loadDataList,
loading: true,
});
expect(rendered).toMatchSnapshot();
});
});

React. Managing state with onClick function using index

Okay, So I am still fairly new to coding. I am trying to manage the state of my calculator using an onClick function. I have a list of ingredients in my database. I have it set up so I can pull all of them from the db and its listed on my component. I am trying to set it so when i click on the individual name it will change the properties listed below. I feel like im on the right track but cant seem to figure it out.
import React, { Component } from 'react';
import $ from 'jquery';
import '../index.css';
class StepFive extends Component{
constructor(props) {
super(props)
this.state = {
ingredients: false,
counter: 0
}
}
componentDidMount() {
var self = this;
$.ajax({
method: 'GET',
url: 'http://localhost:3002/api/ingredients',
})
.done(function(ingredients){
self.setState({ingredients: ingredients})
})
}
handleChange(e){
this.setState({counter: e.target.value})
}
listAll(){
return (
<div>
<div className="vertical-menu">
{this.state.ingredients.map(function(ingredients, i){
console.log(i)
return <div>
<span><a key={i} href="#" onClick={this.handleChange(i)}>{ingredients.name}</a> <a href='#'>+</a></span>
</div>
})}
</div>
</div>)
}
render() {
console.log(this.state, 'in render')
if(this.state.ingredients === false){
return(<div><span>loading</span></div>)
} else {
return(
<div className='stepFiveBox'>
{this.listAll()}
<ul>
<li>Hardness=
{this.state.ingredients[this.state.counter].hardness}</li>
<li>Cleansing={this.state.ingredients[this.state.counter].cleansing}</li>
<li>Condidtion={this.state.ingredients[this.state.counter].condition1}</li>
<li>Bubbly={this.state.ingredients[this.state.counter].bubbly}</li>
<li>Creamy={this.state.ingredients[this.state.counter].creamy}</li>
<li>Iodine={this.state.ingredients[this.state.counter].iodine}</li>
<li>INS={this.state.ingredients[this.state.counter].ins}</li>
<li>Lauric={this.state.ingredients[this.state.counter].lauric}</li>
<li>Myristic={this.state.ingredients[this.state.counter].myristic}</li>
<li>Palmitic={this.state.ingredients[this.state.counter].palmitic}</li>
<li>Stearic={this.state.ingredients[this.state.counter].stearic}</li>
<li>Ricinoleic={this.state.ingredients[this.state.counter].rincinoleic}</li>
<li>Oleic={this.state.ingredients[this.state.counter].oleic}</li>
<li>Linoleic={this.state.ingredients[this.state.counter].linoleic}</li>
<li>Linolenic={this.state.ingredients[this.state.counter].linolenic}</li>
<li>NaOH SAP={this.state.ingredients[this.state.counter].sap}</li>
</ul>
</div>
)
}
console.log(this.listAll(),)}
}
export default StepFive
A problem is in your onClick handler. You're doing
<span onClick={this.handleChange(i)}></span>
Which is invoking handleChange at render time.
In order to invoke the function on click, you just pass the function without calling it.
<span onClick={this.handleChange(i)}></span>
In order to pass specific arguments to the function, you have two options:
<span onClick={() => this.handleChange(i)}></span>
or:
<span onClick={this.handleChange.bind(this, i)}></span>
Here's an example using the .bind syntax:
https://codesandbox.io/s/Z461QVox8
can you try to replace this one :
handleChange(e){
this.setState({counter: e.target.value})
}
to this one :
handleChange = (e) => {
this.setState({counter: e.target.value})
}
and replace this one too :
<a key={i} href="#" onClick={this.handleChange(i)}>
to this one :
<a key={i} href="#" onClick={() => this.handleChange(i)}>
Hope can help you :)
it was this issues.
in the listAll function i added a
var self = this;
then switched all this to self. was getting error value undefined... so i realized i didnt need e.target.value i jsut needed e.
changed handelChange function to
handleChange = (e) => {
this.setState({counter: e});
}
badabingbadaboom

Categories