React props function is undefined when testing - javascript

I am building a react app that deals with budgeting and I have written the code for a BillContainer component and an AddBill component.
This is my code:
BillContainer.js
import React from 'react';
import BillList from './BillList';
import AddBill from './AddBill';
class BillContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
bills: [
]
}
this.addBill = this.addBill.bind(this)
}
addBill(bill) {
this.setState((state) => ({
bills: state.bills.concat([bill])
}));
}
render() {
return (
<div>
<AddBill addNew={this.addBill} />
<BillList bills={this.state.bills} />
</div>
)
}
}
export default BillContainer;
and AddBill.js
import React from 'react';
class AddBill extends React.Component {
constructor(props) {
super(props)
this.state = {
newBill: ''
};
this.updateNewBill = this.updateNewBill.bind(this)
this.handleAddNew = this.handleAddNew.bind(this)
}
updateNewBill(e) {
this.setState({
newBill: e.target.value
})
}
handleAddNew(bill) {
this.props.addNew(this.state.newBill)
this.setState({
newBill: ''
})
}
render() {
return (
<div>
<input
type='text'
value={this.state.newBill}
onChange={this.updateNewBill}
/>
<button onClick={this.handleAddNew}> Add Bill </button>
</div>
)
}
}
export default AddBill;
and this is my AddBill.test.js test:
import React from 'react';
import ReactDOM from 'react-dom';
import Enzyme from 'enzyme';
import { shallow, mount, render } from 'enzyme';
import EnzymeAdapter from 'enzyme-adapter-react-16';
import AddBill from '../components/AddBill';
let Sinon = require('sinon')
Enzyme.configure({adapter: new EnzymeAdapter() });
it('Adds a bill to the list', () => {
const clickSpy = Sinon.spy(AddBill.prototype, 'handleAddNew');
const wrapper = shallow(
<AddBill />
);
wrapper.find('button').simulate('click');
expect(clickSpy.calledOnce).toEqual(true)
})
Im trying to test that a new bill gets added when the Add Bill button is clicked. I've passed the addBill function as a prop but the test is throwing the error TypeError: this.props.AddNew is not a function.
How do I prevent the error message and and make this.props.addNew() not undefined?

You can use jest.spyOn like so:
it('Adds a bill to the list', () => {
const wrapper = shallow(
<AddBill addNew={() => {}} />
);
const clickSpy = jest.spyOn(wrapper.instance(), 'handleAddNew');
wrapper.find('button').simulate('click');
expect(clickSpy).toHaveBeenCalledTimes(1);
})

You're not passing an addNew property:
const wrapper = shallow(
<AddBill addNew={yourAddNewFunction} />
);

Related

React Convert Hooks To Class But Get Error

I want to convert react hooks component to class component but I get error.
HOOKS
import React, { useEffect, useRef } from "react";
const App = () => {
const refValt = useRef(null);
const myfuncValt = () => {
console.log("222222222222222222222");
};
useEffect(() => {
setTimeout(() => {
refValt.current.click();
}, 5000); //miliseconds
}, []);
return (
<div>
<div ref={refValt} onClick={myfuncValt}>Valt</div>
</div>
);
};
export default App;
CLASS
import React from "react";
const myfuncValt = () => {
console.log("222222222222222222222");
};
class App extends React.Component {
constructor(props) {
super(props);
const refValt = React.createRef(null);
}
componentDidMount() {
setTimeout(() => {
this.refValt.current.click();
}, 5000);
}
render() {
return (
<div>
<div ref={this.refValt} onClick={myfuncValt}>
Valt
</div>
</div>
);
}
}
export default App;
I get this error =
I try this.refValt.click(); But It doesn't working.
Actually I suspect the differences between createref vs useref.
This:
const refValt = React.createRef(null);
... needs to be this:
this.refValt = React.createRef(null);

Trying to make a test for a reusable component with jest and enzyme

I've been able to set up shallow rendering tests for most of my components. This one is a reusable component with a life cycle method, so I believe I need to use mount. However the test is still failing...
this is the test
import React from 'react';
import { mount } from 'enzyme';
import SingleAdhesive from './single-adhesive';
describe('SingleAdhesive Tests', () => {
it('Renders without creashing', () => {
mount(<SingleAdhesive />)
})
});
this is the component to test
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import { getAdhesives } from '../services/adhesives';
class SingleAdhesives extends Component {
constructor(props) {
super(props);
this.state = {
adhesives: [],
selected: "",
redirect: false
}
}
componentDidMount() {
const { params } = this.props.match;
this.setState({
adhesives: getAdhesives(),
selected: params
})
}
render() {
const { adhesives, selected } = this.state;
const glue = adhesives.filter(adhesive => adhesive.name === selected.id)
return (
<div className="container m-4 p-2">
{glue.map(item =>
<div key={item.name}>
<h3>{item.name}</h3>
<div>Type: {item.type}</div>
<div>Color: {item.color}</div>
<div>Packaging: {item.packaging}</div>
<div>Shelf life: {item['shelf life']}</div>
<div>Advantages: {item.advantages}</div>
</div>
)}
</div>
);
}
}
export default SingleAdhesives;

React Props for Handle Change not a Function

I'm getting props.handleChange is not a function when running the following code. I'm trying to update the state when the checkbox is clicked. The field that is check box is called myNetwork. I thought that when NetworkArray component, which is a parent of Card component, would have access to the functions and state in App? But this is my first React App. Please, what am I doing wrong?
App.JS
import React, {Component} from 'react';
import SignUp from './components/SignUp';
import NetworkArray from './components/NetworkArray';
import {network} from './NetworkData'
import './App.css';
import 'tachyons';
class App extends Component {
constructor() {
super()
this.state = {
network: network,
}
this.handleChange=this.handleChange.bind(this);
}
handleChange(id) {
this.setState(prevState => {
const updatedNetwork = prevState.network.map(netw => {
if (netw.id===id) {
netw.myNetwork = !netw.myNetwork
}
return netw
})
return {
network:updatedNetwork
}
})
}
render() {
return (
<div>
<NetworkArray
network={network}
handleChange = {this.handleChange} />
</div>
);
}
}
export default App;
Card.js
import React from 'react';
const Card = (props) => {
return(
<div className = 'bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<div>
<h3>{props.name}</h3>
<p>{props.company}</p>
<p>{props.phone}</p>
<p>{props.email}</p>
<p>{props.city}</p>
</div>
<div>
MyNetwork
<input
type = "checkbox"
checked={props.myNetwork}
onChange={()=> props.handleChange(props.id)}
/>
</div>
</div>
)
}
export default Card;
NetworkArray.js
import React, {Component} from 'react';
import Card from './Card';
const NetworkArray = ({network}) => {
const cardComponent = network.map((user,i) => {
return(
<Card
key = {network[i].id}
name = {network[i].firstName + ' ' + network[i].lastName}
company = {network[i].company}
phone= {network[i].phone}
email={network[i].email}
city = {network[i].city}
/>
)
})
return (
<div>
{cardComponent}
</div>
)
}
export default NetworkArray;
You passed the function from App component to NetworkArray component, but not to Card component.
const NetworkArray = ({network, handleChange}) => {
...
<Card
handleChange={handleChange}
...
/>
}

State is not defined

I'm new to react and I'm getting an error for the state and method:
./src/App.js
Line 5: 'state' is not defined no-undef
Line 8: 'inputchangehandler' is not defined no-undef
This is my code until now:
import React from 'react';
import './App.css';
function App() {
state = {
userInput: ''
}
inputchangehandler = (event) => {
this.setState = ({
userInput: event.target.value
})
}
return (
<div className="App">
<input type="text" name="name"
onChange={this.inputchangehandler}
value = {this.state.userInput}/>
</div>
);
}
export default App;
In react there are 2 types of components.
Functional Components(like you used)
Class Components
Functional Components are stateless(in older versions, you can use hooks now) components. So if you want to directly use state you should change your components to class based component like this:
import React, { Component} from 'react';
import './App.css';
class App extends Component {
state = {
userInput: ''
}
inputchangehandler = (event) => {
this.setState = ({
userInput: event.target.value
})
}
render(){
return (
<div className="App">
<input type="text" name="name"
onChange={this.inputchangehandler}
value = {this.state.userInput}/>
</div>
);
}
}
export default App;
Functional component don't have state, form React 16.8 we have Hooks.
You should use useState hook for state.
import React, {useState} from 'react';
const [userInput, setUserInput] = useState('')
Usage,
<input type="text" name="name"
onChange={inputchangehandler}
value = {userInput}/>
inputchangehandler function should be,
const inputchangehandler = (event) => {
setUserInput(event.target.value)
}
Demo
Note: Functional component don't have access to this.
You have created functional component which does not have state. Define App as class component like below :
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
userInput: ''
}
}
inputchangehandler = (event) => {
this.setState = ({
userInput: event.target.value
})
}
render() {
return (
<div className="App">
<input type="text" name="name"
onChange={this.inputchangehandler}
value={this.state.userInput} />
</div>
);
}
}
export default App;
You need to define state in your class's constructor like below:
constructor(props) {
super(props);
this.state = {
userInput: ''
}
}
Declaring your component as a function you don't have state.
Try converting it in class component or in a function using Hooks according to react documentation.
If you are using React version greater than 16.8 then you can use the useState hook.
import React from 'react';
import './App.css';
function App() {
const [userInput, setUserInput] = useState(0);
inputchangehandler = (event) => {
setUserInput(event.target.value)
}
return (
<div className="App">
<input type="text" name="name"
onChange={this.inputchangehandler}
value = {userInput}/>
</div>
);
}
export default App;
Refer: https://reactjs.org/docs/hooks-state.html
If you are using an older version then you will need to convert it to a React.Component
If you don't want to write a class component, you should use hooks, so your code will be like this:
import React, { useState } from 'react';
import './App.css';
function App() {
const [userInput, setUserInput] = useState(undefined);
inputchangehandler = (event) => {
setUserInput(event.target.value)
}
return (
<div className="App">
<input type="text" name="name"
onChange={this.inputchangehandler}
value = {userInput}/>
</div>
);
}
export default App;
i was on React version 17 this worked for me for state undefined error:
Adding example from React official docs:
class Clock extends React.Component { constructor(props) {
super(props);
this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
); } }
Assuming you're working with a class component rather than a functional component (as mentioned in other answers), the error state is not defined can occur if you forgot to prefix it with this..
Instead of:
myFunction() {
let myVariable = state.myStateKey;
// ...
}
Do:
myFunction() {
let myVariable = this.state.myStateKey;
// ...
}

onchange input and set state not working?

I'm working on simple form element using react-js.
There are three component:
App
TakeInput
Index
problem is when user put text in input field setState() function not work properly and data not updated. For testing purpose when i'm placing console.log in app js component it shows undefined on console. anyone sort this please. I want to console the updated data when state update
App.js
import React, { Component } from 'react';
import InputField from './TakeInput';
class App extends Component {
state = {
userInp : '',
outText : ''
}
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}))
console.log(this.userInp);
}
render() {
return (
<div className="App">
<InputField changingVal={this.handlechanger2}/>
</div>
);
}
}
export default App;
TakeInput.JS
import React, { Component } from 'react';
class TakeInput extends Component{
state={
txt: ''
}
handlerChange = (e)=>{
const { changingVal } = this.props;
const v = document.getElementById("userInput").value;
changingVal(v);
// console.log(e.target.value);
this.setState({ txt: e.target.value })
}
render(){
return(
<input type="text" name="userInput" id="userInput" placeholder="Please Enter Text" onChange={this.handlerChange} value={this.txt}/>
)
}
}
export default TakeInput;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
it is about you are developing wrong way. I think you text input should be at your parent component
To read from state you should use this.state.abc
import React, { Component } from 'react';
class TakeInput extends Component{
handlerChange = (e)=>{
this.props.onChange(e.target.value);
}
render(){
return(
<input type="text" name="userInput" placeholder="Please Enter Text" onChange={this.handlerChange} value={this.props.txt}/>
)
}
}
export default TakeInput;
import React, { Component } from 'react';
import InputField from './TakeInput';
class App extends Component {
state = {
userInp : '',
outText : ''
}
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}))
console.log(this.state.userInp);
}
render() {
return (
<div className="App">
<InputField txt={this.state.userInp} onChange={this.handlechanger2}/>
</div>
);
}
}
export default App;
You're trying to console.log this.userInp. It should be this.state.userInp.
Also to see the update right after the last set state, you can do the following:
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}), function(){ console.log(this.state.userInp);}) // set a callback on the setState
}

Categories