React function - is not defined no-undef - reactjs

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;

Related

delete three level component(a component have an array of component, each have an array of compoent)

I would appreciate it greatly if you could let me know how to deal with this problem.
I hava a page component which has an array of company component, and each company has an array of contract.
If I delete any company, every company component will re-render, so I cant put array of contract under each company state, so I put it under page component state.
The question is If I delete one company, how can I correctly re-render all contracts under each component.
Thank you for reading my problem, and sorry for my poor English:(
Error Message is "TypeError: Cannot read property 'contractList' of undefined"
My page code is...
class IRPage extends Component {
// // initialize our state
state = {
companyList: [],
};
addCompanyArr = (newCompany) => {
this.setState(
state => {
const list = state.companyList.push(newCompany);
return {
list,
};
}
)
};
addContractArr = (index, newContract) => {
this.setState(
state => {
const list = state.companyList[index].contractList.push(newContract);
return {
list,
};
}
);
}
setCompanyArr = () => {
this.setState(
state => {
const list = state.companyList;
return {
list,
};
}
)
};
render() {
return (
<div className="container m-5">
<IRContent companyList={this.state.companyList} setCompanyArr={this.setCompanyArr} addCompanyArr={this.addCompanyArr} addContractArr={this.addContractArr}></IRContent>
</div>
)
}
}
export default IRPage;
My content code is ...
class IRContent extends React.Component {
constructor(props) {
super(props);
}
addCompany = () => {
const companyNode = <IRCompany companyList={this.props.companyList} setCompanyArr={this.props.setCompanyArr} index={this.props.companyList.length} addContractArr={this.props.addContractArr}/>;
const newCompany = {
node: companyNode,
contractList: [],
};
this.props.addCompanyArr(newCompany);
}
render() {
return(
<div className="container col-sm-12 justify-content-center">
<h1 align="center">data</h1>
<hr/>
{
this.props.companyList.map((element, index) => {
return <div key={"myCompanyKey_"+index+"_"+this.props.companyList.length} id={index}>{element.node}</div>;
})
}
<button color="primary" onClick = {this.addCompany}>
add new company
</button>
</div>
);
}
}
export default IRContent;
My company code is...
class IRCompany extends React.Component {
constructor(props) {
super(props);
}
deleteCompany = event => {
event.preventDefault();
var targetID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.getAttribute("id")
this.props.companyList.splice(targetID, 1);
this.props.setCompanyArr();
};
addContract = event => {
event.preventDefault();
var newContract = <IRContract/>;
var targetID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.getAttribute("id");
this.props.addContractArr(targetID, newContract);
this.forceUpdate();
};
render() {
return(
<div>
{
this.props.companyList[this.props.index].contractList.map((element, index) => {
return <React.Fragment key={"myContractKey" + index + "_" +this.props.companyList[this.props.index].contractList.length}>{element}</React.Fragment>;
})
}
<button color="primary" onClick = {this.addContract}>主約</button>
</div>
);
}
}
export default IRCompany;
I can successively add company and contract, but there are some problem on deleing.
Thank you for reading my problem, and sorry for my poor English:(

React: triggering method inside HOC component

What I want to do, is create a HOC that has a method that can be triggered by whatever Parent Component is using that HOC to wrap.
For this HOC, I'm trying to fade out the HOC and any components inside it:
HOC:
export function fadeOutWrapper(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
showElement: true,
removeElement: false,
};
}
_triggerFade = () => {
this._fadeOut(this.props.time).then(time => this._removeElement(time));
}
_fadeOut = time => {
let _this = this;
return new Promise((resolve, reject) => {
_this.setState({
showElement: false
});
setTimeout(() => {
resolve(time);
}, time);
});
};
_removeElement = time => {
let _this = this;
setTimeout(() => {
_this.setState({
removeElement: true
});
}, time + 500);
};
render() {
return this.state.removeElement ? null : (
<div
className={
this.state.showElement
? "cfd-container"
: "cfd-container cfd-fadeout"
}
>
<WrappedComponent {...this.props} />
</div>
);
}
};
}
How this component is being used in parent component:
import ComponentToBeFaded from '...';
import { fadeOutWrapper } from '...';
const WrappedComponent = fadeOutWrapper(ComponentToBeFaded);
class ParentComponent extends Component {
const...
super...
handleChildClick = () => {
// ? how to trigger the HOC _triggerFade method?
// WrappedComponent._triggerFade()
}
render() {
return (
<WrappedComponent time={1000} handleClick={this.handleChildClick} {...other props component needs} />
)
}
}
What I want to be able to do is call a method that is inside the HOC, can't seem to check for a change in props inside the HOC... only inside the HOC's render()
Need to keep writing more to meet the submission quota. Any thoughts on how to do this is appreciated. Hope your day is going well!
You don't need showElement in local state of the wrapped component because it's not controlled by that component. Pass it as props and use componentDidUpdate to start fading out.
const { Component, useState, useCallback } = React;
const Button = ({ onClick }) => (
<button onClick={onClick}>Remove</button>
);
function App() {
const [show, setShow] = useState(true);
const onClick = useCallback(() => setShow(s => !s), []);
return (
<WrappedButton
time={1000}
onClick={onClick}
showElement={show}
/>
);
}
function fadeOutWrapper(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
removeElement: false,
fadeout: false,
};
}
componentDidUpdate(prevProps) {
if (
this.props.showElement !== prevProps.showElement &&
!this.props.showElement
) {
this._triggerFade();
}
}
_triggerFade = () => {
this._fadeOut(this.props.time).then(() =>
this._removeElement()
);
};
_fadeOut = time => {
this.setState({ fadeout: true });
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
};
_removeElement = time => {
this.setState({
removeElement: true,
});
};
render() {
return this.state.removeElement ? null : (
<div>
{JSON.stringify(this.state)}
<WrappedComponent {...this.props} />
</div>
);
}
};
}
const WrappedButton = fadeOutWrapper(Button);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How do I manage my array of children components' states?

I'm new to react, so forgive me. I'm having a problem understanding states, specifically those of children.
Purpose: I'm trying to create a form that a user can append more and more components -- in this case, images.
What happens: User appends 2 or more images. User tries to upload an image with UploadButton component, but both the images are the same. I believe this has to do with both appended children sharing the same state.
Question: How do I give each appended child its own image without affecting the other appended children?
class Page extends Component
constructor (props) {
super(props);
this.state = {
id: '',
numChildren: 0,
images: [],
}
this.onAddChild = this.onAddChild.bind(this);
}
showModal() {
this.setState({
numChildren: 0,
images: [],
});
}
renderModal()
const children = [];
//Here's my array of child components
for(var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent key={i} />);
}
return (
<ReactModal>
<this.ParentComponent addChild={this.onAddChild}>
{children}
</this.ParentComponent>
</ReactModal>
)
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
})
}
ParentComponent = (props) => (
<div>
{props.children}
<Button onClick={props.addChild}>Add Item</Button>
</div>
);
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={this.state.images}
onUploadComplete={uri => this.setState({images: uri})}
/>
</div>
);
}
Here's the code for UploadButton:
import React, { Component } from 'react';
import uuid from 'uuid';
import firebase from '../config/firebase';
class UploadButton extends Component {
constructor(props) {
super(props);
this.state = {
isUploading: false
}
}
handleClick() {
const input = document.createElement("INPUT");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/gif, image/jpeg, image/png");
input.addEventListener("change", ({target: {files: [file]}}) => this.uploadFile(file));
input.click();
}
uploadFile(file) {
console.log('F', file);
const id = uuid.v4();
this.setState({ isUploading: true })
const metadata = {
contentType: file.type
};
firebase.storage()
.ref('friends')
.child(id)
.put(file, metadata)
.then(({ downloadURL }) => {
this.setState({ isUploading: false })
console.log('Uploaded', downloadURL);
this.props.onUploadComplete(downloadURL);
})
.catch(e => this.setState({ isUploading: false }));
}
render() {
const {
props: {
value,
style = {},
className = "image-upload-button",
},
state: {
isUploading
}
} = this;
return (
<div
onClick={() => this.handleClick()}
className={className}
style={{
...style,
backgroundImage: `url("${this.props.value}")`,
}}>
{isUploading ? "UPLOADING..." : !value ? 'No image' : ''}
</div>
);
}
}
export default UploadButton;
I tried to exclude all unnecessary code not pertaining to my problem, but please, let me know if I need to show more.
EDIT: This is my attempt, it doesn't work:
//altered my children array to include a new prop
renderModal() {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent imageSelect={this.onImageSelect} key={i} />);
}
//...
};
//my attempt to assign value and pass selected image back to images array
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={uri => this.props.onImageSelect(uri)} //my greenness is really apparent here
onUploadComplete={uri => this.setState({images: uri})}
/>
//...
</div>
);
//added this function to the class
onImageSelect(uri) {
var el = this.state.images.concat(uri);
this.setState({
images: el
})
}
I know I'm not accessing the child prop correctly. This is the most complexity I've dealt with so far. Thanks for your time.
When you write this.state in Child / Parent component, you are actually accessing the state of Page. Now, I would recommend that you pass in the index of the child to the Child like so
children.push(<this.ChildComponent key={i} index={i}/>)
so that each children deals with only its own image like so
ChildComponent = ({index}) => (
<div>
<UploadButton
storage="menus"
value={this.state.images[index]}
onUploadComplete={uri => {
let images = this.state.images.slice()
images[index] = uri
this.setState({images})
}}
/>
</div>
);

React: call setState from another component

This is the parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
news: ""
}
}
componentDidMount() {
this.updateNews();
}
updateNews = () => {
...
}
render() {
<CustomButton type="primary" />
}
This is the CustomButton:
const CustomButton = (props) => {
const {
type
} = props;
const updateItem = () => {
... // The firing of the setState should be here
}
return (
<Button
type={type}
onClick={() => {
updateItem();
}}
>{value}
</Button>
);
How can I fire from inside const updateItem = () => { in CustomButton, so that Parent runs updateNews or componentDidMount?
Use the componentDidUpdate in Parent component like this.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
news: "",
fetchToggle:true
}
}
componentDidMount() {
this.updateNews();
}
componentDidUpdate(prevprops,prevstate){
if(prevstate.fetchToggle!==this.state.fetchToggle){
this.updateNews();
}
}
updateNews = () => {
...
}
fetchToggle=()=>{
this.setState({
fetchToggle:!this.state.fetchToggle;
})
}
render() {
<CustomButton type="primary" fetchToggle={this.fetchToggle} />
}
In the child component clicking on button call this toggle function.
const CustomButton = (props) => {
const {
type
} = props;
const updateItem = () => {
... // The firing of the setState should be here
}
return (
<Button
type={type}
onClick={() => {
props.fetchToggle()
}}
>{value}
</Button>
);
Remember that a toggling value in state is a cleaner and elegant way to update or fetch latest data on every click.
You should pass a callback function to the CustomButton, something like that:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
news: ""
}
}
componentDidMount() {
this.updateNews();
}
updateNews = () => {
...
}
render() {
<CustomButton type="primary" stateUpdatingCallback={(val) => {this.setState({myVal: val})}}/>
}
const CustomButton = (props) => {
const {
type
} = props;
const updateItem = () => {
... // The firing of the setState should be here
}
return (
<Button
type={type}
onClick={() => {
this.props.stateUpdatingCallback("somevalue")
}}
>{value}
</Button>
);

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

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>
);
}
}

Resources