React/Jest How to test function outside of the class component - reactjs

So in the example below i have validateResult which is set to the displayMessage. Depending on the user input it'd return a value, this is outside of the class component and i dont know how to test a function outside of the class with jest.
So i tried using mount from enzyme to mount the component then with instance to access the function but this gave me an error saying that this is not a function and im not sure how to test this.
test.js
const wrapper = mount (
<tempComponent />,
);
const instance = wrapper.instance();
it('expect result to be good', () => {
expect(instance.validateResult(true)).toBe("good");
});
tempComponent.js
const validateResult = (data) => {
if(data)
return "good";
else
return "bad";
};
class tempComponent extends Component {
constructor(props) {
super(props);
this.state = { inputdata: '' };
this.onSuccess = this.onSuccess.bind(this);
}
render() {
const { inputdata } = this.state;
const { onSubmit } = this.props;
const displayMessage = validateResult(inputdata);
return (
<div id="submit-form" className="row justify-content-center">
<div className="col-md-4">
<FormContainer onSubmit={() => onSubmit({ inputdata }, this.onSuccess)} >
<Input type="text" label="" onTextChange={(value) => this.setState({ ...this.state, inputdata: value })} text={inputdata} />
<SubmitButton value={'Submit'} disabled={displayMessage}/>
</FormContainer>
</div>
</div>
);
}
}

Related

TestDont - Change Username - REACT

Looking for thinking tips towards refactoring the App function. The component must remain unchanged. This example is clunky and a mashup of several different online contributions to the use of ref.
I started here: https://reactjs.org/docs/refs-and-the-dom.html
Thanks in advance.
class Username extends React.Component {
state = { value: "" };
changeValue(value) {
this.setState({ value });
}
render() {
const { value } = this.state;
return <h1>{value}</h1>;
}
}
function App() {
this.username = React.useRef();
this.component = React.useRef()
clickHandler = e => {
//console.log(this.component.current.changeValue())
this.component.current.changeValue(this.username.current.value)
}
return (
<div>
<button onClick={clickHandler}>Change Username</button>
<input type="text" ref={this.username}/>
<Username ref={this.component}/>
</div>
);
}
document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
document.querySelector("input").value = "John Doe";
document.querySelector("button").click();
setTimeout(() => console.log(document.getElementById("root").innerHTML));
Try this code.
function Username({ value }) {
return (
<h1>{value}</h1>
);
}
class App extends React.Component {
state = {
usernameDynamic: '',
usernameStatic: '',
}
onChangeUserName = () => {
this.setState({ usernameStatic: usernameDynamic });
}
onChangeUserNameDynamic = (e) => {
this.setState({ usernameDynamic: e.target.value });
}
render() {
return (
<div>
<button onClick={this.onChangeUserNameStatic}>Change Username</button>
<input type="text" value={this.state.usernameDynamic} onChange={this.onChangeUserNameDynamic} />
<Username value={this.state.usernameStatic} />
</div>
);
}
}

How to setState to answer from APi and use map

Im trying to create recipes searcher. In App.js I receive query from search input from another component and I want to setState to answer from APi. Console.log from callback in setState shows updated state but the state is not updated. I need setState updaed so I can use map on it and display list of recipes in render. It gives me error map is not a function because this.state.recipesList is still empty. Anyone can help me ?
class App extends Component {
state = {
query: "",
recipesList: []
};
getQuery = query => {
const key = "2889f0d3f51281eea62fa6726e16991e";
const URL = `https://www.food2fork.com/api/search?key=${key}&q=${query}`;
fetch(URL)
.then(res => res.json())
.then(res => {
this.setState(
{
recipesList: res
},
() => {
console.log(this.state.recipesList);
}
);
});
console.log(this.state.recipesList);
};
render() {
const test = this.state.recipesList.map(item => {
return (
<div className="recispesList">
<h1>{item.title}</h1>
</div>
);
});
return (
<div className="App">
<Search query={this.getQuery} />
<div className="contentWrapper">{}</div>
</div>
);
}
}
Search component:
class Search extends Component {
state = {
searchValue: ""
};
handleChange = val => {
let searchValue = val.target.value;
this.setState({
searchValue
});
};
handleSubmit = e => {
e.preventDefault();
this.setState({
searchValue: ""
});
this.props.query(this.state.searchValue);
};
render() {
return (
<div className="searchWrapper">
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.searchValue} />
<button />
</form>
</div>
);
}
}
export default Search;
It seems that instead of directly assigning the whole response to recipesList:
this.setState(
{
recipesList: res
},
() => {
console.log(this.state.recipesList);
}
);
you need to get recipes array first via res.recipes:
this.setState(
{
recipesList: res.recipes
},
() => {
console.log(this.state.recipesList);
}
);

React function - is not defined no-undef

I get the following error when trying to compile my app 'handleProgress' is not defined no-undef.
I'm having trouble tracking down why handleProgress is not defined.
Here is the main react component
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
this.handleProgress = this.handleProgress.bind(this);
}
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
handleProgress = () => {
console.log('hello');
};
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
Your render method is wrong it should not contain the handlePress inside:
You are calling handlePress on this so you should keep it in the class.
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
this.handleProgress = this.handleProgress.bind(this);
}
handleProgress = () => {
console.log('hello');
};
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
If you are using handleProgress inside render you have to define it follows.
const handleProgress = () => {
console.log('hello');
};
if it is outside render and inside component then use as follows:
handleProgress = () => {
console.log('hello');
};
If you are using arrow function no need to bind the function in constructor it will automatically bind this scope.
handleProgress should not be in the render function, Please keep functions in you component itself, also if you are using ES6 arrow function syntax, you no need to bind it on your constructor.
Please refer the below code block.
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
// no need to use bind in the constructor while using ES6 arrow function.
// this.handleProgress = this.handleProgress.bind(this);
}
// move ES6 arrow function here.
handleProgress = () => {
console.log('hello');
};
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
Try this one, I have check it on react version 16.8.6
We don't need to bind in new version using arrow head functions. Here is the full implementation of binding argument method and non argument method.
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0
};
constructor() {
super();
}
render() {
return (
<div>
<button onClick={this.updateCounter}>NoArgCounter</button>
<button onClick={() => this.updateCounterByArg(this.state.count)}>ArgCounter</button>
<span>{this.state.count}</span>
</div>
);
}
updateCounter = () => {
let { count } = this.state;
this.setState({ count: ++count });
};
updateCounterByArg = counter => {
this.setState({ count: ++counter });
};
}
export default Counter;

How to get the value of an input in multiple items by react

I have a series of data what by clicking a search text,the value of the input that in every item shoude be shown, but it is only show the value of the input of last item.When i changed to below code there would not be any result.
class App extends React.Component {
constructor(props){
super(props);
this.state = {
data: [],
};
$.ajax({
url:"/json.bc",
type:"post",
success:(result)=>{
this.setState({data: eval(result)});
}})
this.handelSearch = this.handelSearch.bind(this);
}
render() {
const { data, currentPage, itemsPerPage } = this.state;
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = data.slice(indexOfFirstItem, indexOfLastItem);
const renderInfo= currentItems.map((item, i) => {
return <div class="item">
<input type="hidden" value={item.name} ref={(ref) => this.name[i] = ref} />
</div>
});
return (
<div>
<input type="hidden" value={this.state.data.length} ref="dateLen" />
<span onClick={this.handelSearch}>search</span>
{renderInfo}
</div>
)};
handelSearch(event){
var dateLen = this.refs.dateLen.value
for(var i=1 ; i<=dateLen;i++){
console.log(this.realname[i].value)
}
}}
ReactDOM.render(<App/>, document.getElementById('Result'));
this is a cleaner way to write this code with,
Can you please follow this pattern.
read the comments below to see what changed:
- there is a function renderAltImages() which doesn't exist in your component!
- you should use defaultValue in your input, instead of value unless you are going to make these inputs controlled.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const $ = require("jquery");
class App extends React.Component {
constructor(props) {
// in constructors we define an initialState for our component.
super(props);
this.state = {
data: []
};
// here we bind our function to the Component Scope.
this.handelSearch = this.handelSearch.bind(this);
}
componentDidMount() {
// in this Component Life Cycle (componentDidMount), we make our fetch requests.
$.ajax({
url: "/json.bc",
type: "post",
success: result => {
this.setState({ data: eval(result) });
}
});
}
handelSearch(event) {
var dateLen = this.refs.dateLen.value;
for (var i = 1; i <= dateLen; i++) {
console.log(this.realname[i].value);
}
}
render() {
const { data, currentPage, itemsPerPage } = this.state;
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = data.slice(indexOfFirstItem, indexOfLastItem);
const renderInfo = currentItems.map((item, i) => {
return (
<div class="item">
<input
type="hidden"
value={this.renderAltImage(item.hotelinfo.hotelsearch)} //there is no renderAltImage() function in your componenet,, where is it?
ref={ref => (this.realname[i] = ref)}
/>
</div>
);
});
// you named this renderHotels, while you printed renderInfo below, so I changed it to renderInfo instead.
return (
<div>
<input type="hidden" value={this.state.data.length} ref="dateLen" />
<span onClick={this.handelSearch}>search</span>
{renderInfo}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

redux-form always returns same values for Multiselect react-widget

I am trying to use redux-form with react-widget Multiselect this example:
var Multiselect = ReactWidgets.Multiselect
, people = listOfPeople();
var Example = React.createClass({
getInitialState() {
return { value: people.slice(0,2) };
},
_create(name){
var tag = { name, id: people.length + 1 }
var value = this.state.value.concat(tag)
// add new tag to the data list
people.push(tag)
//add new tag to the list of values
this.setState({ value })
},
render(){
// create a tag object
return (
<Multiselect data={people}
value={this.state.value}
textField="name"
onCreate={this._create}
onChange={value => this.setState({ value })}/>
)
}
});
ReactDOM.render(<Example/>, mountNode);
Below is a code snippet for a parent component which makes usage of redux-form (EditVideo component) component (please look at the comments in onSubmit method):
class VideoEdit extends React.Component {
constructor(props) {
super(props);
}
onSubmit = (values) => {
console.log(values.categories) // always returns initialValues for categories, new values not adding
}
render() {
const { loading, videoEdit, categories } = this.props;
if (loading) {
return (
<div>{ /* loading... */}</div>
);
} else {
return (
<div>
<h2>Edit: {videoEdit.title}</h2>
<EditVideo
onSubmit={this.onSubmit}
initialValues={videoEdit}
categories={categories}
/>
</div>
);
}
}
}
And here is a code snippet of redux-form component with react-widget Multiselect component:
class CategoryWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.defValue,
extData: this.props.data
}
this._create = this._create.bind(this);
}
_create(name) {
var tag = { name, id: this.state.extData.length + 100 + 1 }
var value = this.state.value.concat(tag)
var extData = this.state.extData.concat(tag)
this.setState({
extData,
value
})
}
render() {
return (
<Multiselect
{...this.props.input}
data={this.state.extData}
onBlur={() => this.props.input.onBlur()}
value={this.state.value || []}
valueField="id"
textField="name"
onCreate={this._create}
onChange={value => this.setState({ value })}
/>
)
}
}
const EditVideoForm = (props) => {
const { handleSubmit, submitting, onSubmit, categories, initialValues, defBook } = props;
return (
<Form name="ytvideo" onSubmit={handleSubmit(onSubmit)}>
<div>
<Field
name="categories"
component={CategoryWidget}
data={categories}
defValue={initialValues.categories}
/>
</div>
<br />
<Button color="primary" type="submit" disabled={submitting}>
Submit
</Button>
</Form>
);
};
export default reduxForm({
form: 'videoEdit',
enableReinitialize: true
})(EditVideoForm);
The Multiselect widget works as expected, yet the form on submit always returns the same initial values for categories.
I believe the problem lays in the fact that CategoryWidget is a class base component? If so, what is a way to make it work?
Here is what I have done for my Multiselect at the end:
class CategoryWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.defValue,
extData: this.props.data
}
this._create = this._create.bind(this);
}
_create(name) {
var tag = { name, id: this.state.extData.length + 100 + 1 }
var value = this.state.value.concat(tag)
var extData = this.state.extData.concat(tag)
this.setState({
extData,
value
})
}
componentDidUpdate() {
let { onChange } = this.props.input
onChange(this.state.value)
}
handleOnChange(value) {
this.setState({ value })
}
render() {
const input = this.props.input
return (
<Multiselect
{...input}
data={this.state.extData}
onBlur={() => input.onBlur()}
value={this.state.value || []}
valueField="id"
textField="name"
onCreate={this._create}
onChange={value => this.handleOnChange(value)}
/>
)
}
}

Resources