Jest: How to test function that calls bind(this) - reactjs

I have a Parent component that looks like this:
export class Header extends Component {
constructor(props) {
super(props)
this.state = { activeTab: TAB_NAMES.NEEDS_REVIEW }
}
filterByNeedsReview() {
const { filterByNeedsReviewFn } = this.props
this.setState({ activeTab: TAB_NAMES.NEEDS_REVIEW })
filterByNeedsReviewFn()
}
render() {
return (
...
<FilterTab
...
onClick={this.filterByNeedsReview.bind(this)}
/>
...
)
}
}
I'm trying to test that the child was loaded with the right props. Originally I had it as an anonymous function: onClick={ () => this.filterByNeedsReview() } but I couldn't test that so I tried to move on to bind(this) instead.
However, I'm having issues mocking out the bind function:
it('renders a filter tab with the right props for needs review', () => {
const bounded = jest.fn()
const boundedFilterByNeedsReview = jest.fn(() => {
return { bind: bounded }
})
Header.prototype.filterByNeedsReview = boundedFilterByNeedsReview
expect(
shallowRender()
.find(FilterTab)
.findWhere(node =>
_.isMatch(node.props(), {
... // other props
onClick: bounded, //<-------------- FAILS WHEN I ADD THIS LINE
})
)
).toHaveLength(1)
})

Bind the method in the constructor to prevent the method from re-binding every time the component renders:
constructor(props) {
super(props)
this.state = { activeTab: TAB_NAMES.NEEDS_REVIEW }
this.filterByNeedsReview = this.filterByNeedsReview.bind(this)
}
Then pass the bound method as a prop to the child:
<FilterTab
...
onClick={this.filterByNeedsReview}
/>
You don't need to use an anonymous function here.

Related

Rendering a new component inside componentDidMount - React

I will have to render a new component after all the expected components are loaded. I will need a timeout based on which the the new component has to be rendered. So this new component has to show up after 5 minutes after the page has loaded.
I need to render a component called new_component that extends React.component
public componentDidMount(): void {
if (visited) {
setTimeout(() => {
console.log('Reached the timeout')
//Render the new conponent here. (Not sure how to call the render function of another component here)
}, timeout);
}
Can someone help me call the render function of new_component inside componentDidMount please. i tried new_component.render(). But that does not seem to work.
You can use state to track this.
componentDidMount() {
setTimeout(() => {
this.setState({ showNewComponent: true })
})
}
and in render:
render() {
if (this.state.showNewComponent) {
return <NewComponent />
}
return null
}
You can go with this code, wait and then render new one:
cosnt FIVE_MIN = 5 * 60 * 1000
class Example {
this.state = { isShowComponent: false }
timer
componentDidMount() {
this.timer = setTimeout(() => {
this.setState({ isShowComponent: true })
}, FIVE_MIN)
}
componentWilllUnmount() {
clearTimeout(this.timer)
}
render() {
if (this.state.isShowComponent) return <NewComponent />
return <Component />
}
}
:)
you can render your component by your state.
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
isTimeout: false,
};
}
componentDidUpdate(prevProps, prevState) {
this.checkTimeout = setTimeout(() => {
this.setState(() => ({isTimeout: true}))
}, 500);
}
componentWillUnmount() {
// clean it up when the component is unmounted.
clearTimeout(this.checkTimeout);
}
render () {
if (isTimeout) {
return (k<h1>time is running out</h1>)
}
return (<h1>hello world.</h1>)
}
}

How to use same setstate method to set state of different variables to true / false using react?

I have to set the state to true or false based on certain conditions. I have the state defined in parent component and I set its state to true or false by calling a method from child component.
Below is the code,
class ParentComponent extends React.purecomponent {
state = {
first: false,
second: false,
};
set_first_to_true =() => {
this.setState({first: true});
}
set_first_to_false =() => {
this.setState({first: false});
}
set_second_to_true =() => {
this.setState({second: true});
}
set_second_to_false =() => {
this.setState({second: false});
}
render() {
return (
<ChildComponent
set_first_to_true={this.set_first_to_true}
set_first_to_false={this.set_first_to_false}
set_second_to_true={this.set_second_to_true}
set_second_to_false={this.set_second_to_false}/>)
}
}
class ChildComponent extends react.purecomponent {
componentDidUpdate () {
this.props.set_first_to_true();
this.props.set_first_to_false();
this.props.set_second_to_true();
this.props.set_second_to_false();
}
}
Now as you see from above code, there is duplication of code. how can I refactor this such that there is no repetition? Thanks.
You can reuse one state update handler like this:
// Parent
...
handleUpdate = (name, value) => {
this.setState({ [name]: value });
};
...
render() {
return (
<Child onUpdate={this.handleUpdate} />
);
}
// Child
...
this.props.onUpdate('first', true);
this.props.onUpdate('first', false);
this.props.onUpdate('second', true);
...
define:
changeState = (stateName, Value) => this.setState({[stateName]: Value})
use in child:
changeState('first',true)
The easiest solution is passing a correctly bound callback to parents setState. Other answers do the same, but they miss the binding.
class ParentComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
first: false,
second: false
};
this.setStateRemote = this.setStateRemote.bind(this)
}
setStateRemote(item){
this.setState(item)
}
render() {
return (
<ChildComponent setParentState={this.setStateRemote}
/>
);
}
}
class ChildComponent extends React.PureComponent {
componentDidUpdate() {
this.props.setParentState({first: true})
...
}

How can I test a component click which requires a function from a parent component?

I'm trying to write tests for my Reactjs application ( I'm very new to testing this ), but I can't seem to grasp how it works properly.
Below is my Component, what I want to test is handleRegisterFieldClick().
I've seen examples of mocking a click function but since the function I need to call is one passed from a parent component, how can I simulate that? I want to check if state.class = 'clickedSquare' after calling handleRegisterFieldClick().
export default class Square extends Component {
constructor(props) {
super(props)
this.state = {
fieldcell: props.field,
value: props.value,
class: props.class,
xPos: props.xPos,
yPos: props.yPos,
clicked: props.clicked
}
this.handleClick = this.handleClick.bind(this);
this.handleRegisterFieldClick = this.handleRegisterFieldClick.bind(this);
}
handleClick() {
if(this.state.fieldcell){
if (this.props.clicked) {
this.setState({
class: 'square'
})
} else {
this.setState({
class: 'clickedSquare'
})
}
}
}
handleRegisterFieldClick(){
if(this.state.fieldcell){
this.handleClick()
var params = {
xPos: this.state.xPos,
yPos: this.state.yPos
}
this.props.registerFieldClick(params)
}
}
render() {
return (
<button className={this.state.class} onClick={this.handleRegisterFieldClick} background={this.state.backgroundcolor}>
{this.state.value !== 0 ? this.state.value : null}
</button>
);
}
}
Perhaps something like this
const timeout = 'some value';
const timeout2 = 'some other value';
componentDidMount() {
setTimeout(() => {
this.handleRegisterFieldClick();
setTimeout(() => {
//Since setState is asynchronous were using a second timeout
//check the state.class condition here.
}, timeout2);
}, timeout);
}

ReactJS - Pass Updated Value To Sub-Component Method

I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

Resources