React is a popular library for creating web apps and mobile apps.
In this article, we’ll look at some tips for writing better React apps.
Update the Context Value in Provider from the Consumer
We can update the context value from the provider if we pass in the function into the context.
For instance, we can write:
const MyContext = React.createContext({});
class Child extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
onChange(e){
const name = e.target.value;
this.setState({
name
});
this.props.context.updateValue('name', name);
}
render() {
return (
<React.Fragment>
<input onChange={this.onChange} />
</React.Fragment>
)
}
}
const withContext = (Component) => {
return (props) => {
<MyContext.Consumer>
{(context) => {
return <Component {...props} context={context} />
}}
</MyContext.Consumer>
}
}
Child = withContext(Child)
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
name: "bar",
};
}
updateValue = (key, val) => {
this.setState({[key]: val});
}
render() {
return (
<MyContext.Provider value={{ state: this.state, updateValue: this.updateValue }}>
<Child />
</MyContext.Provider>
)
}
}
We create the context and pass the value from the Parent
to the components in the context.
The object has the state
and the updateValue
function.
We then get the updateValue
method from the props.context
property, which is what we have.
Then we set the name by calling the updateValue
method to set the name
state of the Parent
.
We’ve to remember to add the MyContext.Consumer
to whatever component is consuming the context.
To do that, we created the withContext
higher-order component to wrap any component with the context consumer.
getInitialState for React Class
We put the initial state in the constructor of a class component.
For instance, we can write:
import { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
foo: true,
bar: 'no'
};
}
render() {
return (
<div className="theFoo">
<span>{this.state.bar}</span>
</div>
);
}
}
We set the foo
and bar
states’ initial values in the constructor.
Using Local Storage with React
We can use the native local storage methods in React.
For instance, we can write:
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props);
const storedClicks = 0;
if (localStorage.getItem('clicks')) {
storedClicks = +(localStorage.getItem('clicks'));
}
this.state = {
clicks: storedClicks
};
this.click = this.click.bind(this);
}
onClick() {
const newClicks = this.state.clicks + 1;
this.setState({ clicks: newClicks });
localStorage.setItem('clicks', newClicks);
}
render() {
return (
<div>
<button onClick={this.onClick}>Click me</button>
<p>{this.state.clicks} clicks</p>
</div>
);
}
}
We get the clicks from local storage if it exists.
Then we parse it if it exists and set that as the initial value of the clicks
state.
Then in the render
method, we have a button to update the clicks
state and the local storage clicks
value with the onClick
method.
It updates the clicks
state.
Also, we update the local storage’s clicks
value after that.
Test a React Component Function with Jest
We can mock functions that our component depend on so that we can do testing with it.
For instance, we can write:
import React, { Component } from 'react';
class Child extends Component {
constructor (props) {
super(props);
};
render() {
const { onSubmit, label} = this.props;
return(
<form onSubmit={onSubmit}>
<Button type='submit'>{label}</Button>
</form >
);
};
};
export default class App extends Component {
constructor (props) {
super(props);
this.label = “foo”;
};
onSubmit = (option) => {
console.log('submitted');
};
render () {
return(
<div className="container">
<Child label={this.label} onSubmit={this.onSubmit} />
</div>
);
};
};
We can then mock the onSubmit
function when we test the child by writing:
import React from 'react';
import { shallow } from 'enzyme';
import Child from '../Child';
const onSubmitSpy = jest.fn();
const onSubmit = onSubmitSpy;
const wrapper = shallow(<Child onSubmit={onSubmitSpy} />);
let container, containerButton;
describe("Child", () => {
beforeEach(() => {
container = wrapper.find("div");
containerButton = container.find(“Button”);
onSumbitSpy.mockClear();
});
describe("<Button> behavior", () => {
it("should call onSubmit", () => {
expect(onSubmitSpy).not.toHaveBeenCalled();
containerButton.simulate(‘click’);
expect(onSubmitSpy).toHaveBeenCalled();
});
});
});
We mount the component with the onSubmit
prop populated by the onSubmitSpy
, which is a mock function.
We tested by simulating a click event on the button of the Child
component.
Then we check if the mocked function is called with toHaveBeenCalled
.
Conclusion
We can pass around data with the context API.
Also, we can mock functions in with Jest so we can pass them in as props to test what we need to test.
Local storage can be used as-is in a React component.