A React Redux cheatsheet. Made by myself, for myself, based on my own requirements & how my head works. If it helps someone else, then awesome! Pull requests with improvements are more than welcome.
NOTE: There may be code snippets below which are not 100% accurate and need adjusting to work correctly. I will be addressing them over time, but for now, this does the job sufficiently.
- Functional Components
- Class Components with Local State
- Children Components
- Rendering Functions
- OnClick Event
- Handling Forms and OnSubmit Event
- Conditionals: If Rendering
- Conditionals: Ternary If Rendering
- Loops: Map a list of elements
- React Router: Routing
- React Router: Routing with Parameters
- Lifecycle Methods
- React Hooks
- React Hooks: Lifecycle Methods
- HTTP Requests
- Enzyme/Snapshot Testing
- Redux: Overview
- Redux: Setting Up
- Redux: Types
- Redux: Actions
- Redux: Reducers
- Redux: Updating State
Functional Components are best used in components that do not require a constructor/local state or the use of lifecycle methods.
<HelloWorld name="John" />
import React from "react";
import "./style.scss";
const HelloWorld = props => {
return (
<div className="danger">Hello {props.name}!</div>
);
};
export default HelloWorld;
Class Components are best used in components that require a constructor/local state or the use of lifecycle methods.
<HelloWorld name="John" />
import React from "react";
import "./style.scss";
class HelloWorld extends React.Component {
constructor(props){
super(props);
this.state = {
city: "",
age: 10
}
}
render() {
const { name } = this.props; // Allows {name} instead of {props.name} below.
return (
<div className="danger">Hello {name}!</div>
);
}
}
export default HelloWorld;
<HelloWorld name="John">
Greetings
</HelloWorld>
import React from "react";
import "./style.scss";
// Prints: "Greetings John!"
const HelloWorld = ({ children }) => {
return (
<div className="danger">{children} {props.name}!</div>
);
};
export default HelloWorld;
const HelloWorld = props => {
return (
<div className="danger">Hello {getUsername()}!</div>
);
};
function getUsername() {
return "John";
}
const HelloWorld = props => {
return (
{printWelcomeMessage()}
);
};
function printWelcomeMessage() {
return (
<div className="danger">Hello John!</div>
);
}
const HelloWorld = props => {
return (
<div className="danger">
Hello <a href="#" onClick={this.showWelcomeAlert}>John!</a>
</div>
);
};
showWelcomeAlert = (e) => {
e.preventDefault();
console.log('Welcome!');
}
Style A
import React from "react";
import "./style.scss";
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleChange = event => {
const { value } = event.target;
this.setState({value: value});
}
handleSubmit = event => {
event.preventDefault();
alert('A name was submitted: ' + this.state.value);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default NameForm;
Style B
import React from "react";
import "./style.scss";
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleSubmit = event => {
event.preventDefault();
alert('A name was submitted: ' + this.state.value);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value}
onChange={({ target: { value }}) => {
this.setState({ value: value});
}}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default NameForm;
render() {
return (
{DisplayMessage()}
);
}
function DisplayMessage() {
const { show } = this.state;
if(show){
return (
<div>Hello World</div>
);
} else {
return (
<div>Goodbye World</div>
);
}
}
render() {
return (
{this.state.show ? <div>Hello World</div> : <div>Goodbye World</div>}
);
}
Loops through cartItems
...
Note: If you are unfamiliar with where cartItems
is coming from or the mapPropsToState()
function, assume cartItems
is just a list for now and ignore react-redux
-related boilerplate.
import React from "react";
import { connect } from "react-redux";
import CheckoutItem from "../../components/checkout-item/checkout-item.component";
import "./checkout.styles.scss";
const CheckoutPage = ({ cartItems }) => {
return (
<div className="checkout-page">
{cartItems.map(cartItem => (
<CheckoutItem key={cartItem.id} cartItem={cartItem} />
))}
</div>
);
};
const mapStateToProps = state => ({
cartItems: state.cart.cartItems
});
export default connect(mapStateToProps)(CheckoutPage);
If a Route has exact
then it will only match the exact specified path. If it does not use exact
then React Router will try and match the closest possible route and still try to navigate the user.
The below in App.js sets up the routes, so that we can <Link />
to them in components.
npm install react-router-dom
import { Switch, Route, Redirect } from "react-router-dom";
class App extends React.Component {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/shop" component={ShopPage} />
<Route
exact path="/signin"
render={() =>
this.props.currentUser ? (
<Redirect to="/" />
) : (
<SignInAndSignUpPage />
)
}
/>
</Switch>
</div>
);
}
}
import { Link } from "react-router-dom";
<div className="options">
<Link className="option" to="/shop">SHOP</Link>
<Link className="option" to="/contact">CONTACT</Link>
</div>
import { Switch, Route } from "react-router-dom";
import ShopComponent from "./shopComponent";
<div>
<Switch>
<Route path="/shop/:shopId" component={ShopComponent}>SHOP</Link>
<Route path="/contact">CONTACT</Link>
</Switch>
</div>
import React from "react";
const ShopComponent = ({ match }) => {
return (
<div>Your shop id is: {match.params.shopId}!</div>
);
};
export default ShopComponent;
Method | Explanation |
---|---|
render() | Renders your component. Occurs during the mounting & updating of a component. |
componentDidMount() | Called as soon as a component is mounted and ready. Good place to initiate initial API calls to populate a component's data from. |
componentDidUpdate() | Called whenever a component is updated. Good place to react to prop or state changes. |
componentWillUnmount() | Called just before a component is unmounted and destroyed. |
shouldComponentUpdate() | Used to provide a bool function, who's output determines whether to update a component or not. |
getDerivedStateFromProps() | Called just before render(), right after props are updated. |
getSnapshotBeforeUpdate() | Called just before a component is updated. The value returned is passed on to componentDidUpdate(). |
React Hooks allow functional components to now utilise and store their own state, which was previously only possible for class components.
import React, { useState } from "react";
import "./style.scss";
const HelloWorld = props => {
const [age, setAge] = useState(0); // age is a variable in the component state
setAge(20); // can now call setAge() method to modify it
return (
<div className="danger">Hello {props.name}!</div>
);
};
export default HelloWorld;
It is also possible to store several variables in state using React Hooks.
import React, { useState } from "react";
import "./style.scss";
const HelloWorld = props => {
const [state, setState] = useState({ age: 0, height: 0 });
setState({ ...state, age: 20 }); // possible to use the spread operator!
return (
<div className="danger">Hello {props.name}!</div>
);
};
export default HelloWorld;
Hook | Replaces/Explanation |
---|---|
useState() |
As shown above |
useEffect() |
componentDidMount , componentDidUpdate & componentWillUnmount |
useContext() |
https://reactjs.org/docs/hooks-reference.html#usecontext |
useReducer() |
https://reactjs.org/docs/hooks-reference.html#usereducer |
Example usage of useEffect()
where whenever state.selectedFeedbackFor
changes, fetchCards()
is called and the DOM is re-rendered as a result:
import React, { useState, useEffect } from "react";
import Card from "../Card/card.component.js";
const Feedback = () => {
const [state, setState] = useState({ feedback: [], selectedFeedbackFor: ""});
const fetchCards = () => {
return fetch(`/api/feedback?id=${state.selectedFeedbackFor}`)
.then(response => {
return response.json();
})
.then(responseJson => {
setState({ ...state, feedback: responseJson });
});
};
useEffect(() => {
fetchCards();
}, [state.selectedFeedbackFor]);
return (
...
);
};
export default Feedback;
npm install axios
axios.get('/user', {
params: {id: 1}
}).then(function (response) {
//some logic here
})
.catch(function (error) {
//some logic here
})
.finally(function () {
//some logic here
});
axios.post('/user', {
firstName: 'Bob',
id: 2
}).then(function (response) {
//some logic here
})
.catch(function (error) {
//some logic here
});
getUsers = async () => {
let res = await axios.get("/user?id=1");
let { data } = res.data;
this.setState({ users: data });
};
// setupTests.js
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
configure({ adapter: new Adapter() });
import { shallow, mount } from "enzyme";
import React from "react";
import toJson from 'enzyme-to-json';
import CustomButton from "./custom-button.component";
import Modal from "./modal.component";
it("expect to render CustomButton component", () => {
expect(shallow(<CustomButton />).length).toEqual(1);
});
it("expect to render CustomButton component snapshot", () => {
expect(shallow(<CustomButton />)).toMatchSnapshot();
});
it("expect to render Google CustomButton component snapshot", () => {
const wrapper = toJson(mount(<CustomButton isGoogleSignIn />));
expect(wrapper.find(".custom-button").hasClass("google-sign-in")).toEqual(true);
});
it("should call closeAction when the close button clicked", () => {
const closeAction = jest.fn();
const wrapper = shallow(
<Modal isOpen={false} closeAction={closeAction}>
Contents of the Modal
</Modal>
);
wrapper.find(".btnDelete").simulate("click");
expect(closeAction.mock.calls.length).toBe(1);
});
React-Redux is the official implementation of Redux for React. Its purpose is to allow React applications to share a global state alongside their own local state. As React applications made up of numerous React components grow, this reduces the need to pass around a large number of props between components.
The core ideas behind Redux are based on ideas from Flux, CQRS & Event Sourcing. The global redux state object is immutable, without setters. This means it is not possible to modify an object in state with a line as simple as this.state.name = "Bob";
such as in alternative similar solutions such as Vuex in the VueJS eco-system. Nor are there setter methods on the state object.
- To change a value within the Redux state, an Action must be despatched.
- Actions contain both a type and a payload. -- Types are unique string identifiers for an action. -- The payload is the new object we want to overwrite an existing value in state, with.
- Reducers listen for Actions. An action will map to a pure function within the reducer. These pure functions take the existing state alongside the Action arguments, then return a new object that represents the updated version of the global Redux state. Most of the time this will be the old global state with changes made to it, reflecting the Action payload. The changes to the DOM are then reflected.
npm install redux
npm install react-redux
// npm install redux-logger #optional
Create a root-reducer.js:
import { combineReducers } from 'redux';
import exampleReducer from './example/example.reducer';
export default combineReducers({
example: exampleReducer
});
Create a store.js:
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import rootReducer from './root-reducer';
const middlewares = [logger];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
In your index.js, import the react-redux Provider and surround your application in it:
import { Provider } from "react-redux";
import { store } from "./redux/store";
import App from "./App";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
export const UserActionTypes = {
SET_CURRENT_USER: "SET_CURRENT_USER"
};
import { UserActionTypes } from "./user.types";
export const setCurrentUser = user => ({
type: UserActionTypes.SET_CURRENT_USER,
payload: user
});
import { UserActionTypes } from "./user.types";
const INITIAL_STATE = {
currentUser: null
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UserActionTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload
};
default:
return state;
}
};
export default userReducer;
import { connect } from "react-redux";
import { toggleCartHidden } from "../../redux/cart/cart.actions";
const CartIcon = ({ toggleCartHidden, cartItemCount }) => {
return (
<div className="cart-icon" onClick={toggleCartHidden}>
<span className="item-count">{cartItemCount}</span>
</div>
);
};
// Sets local component state of cartItemCount
// to equal state.cart.cartItemCount in redux global state
const mapStateToProps = state => ({
cartItemCount: state.cart.cartItemCount
});
// Whenever toggleCartHidden() is called (e.g. onClick)
// Trigger the toggleCartHidden() action...
// This in return triggers a reducer function to run & change global state
// Note: You have to inject actions in the
// functional component props area, as well as importing it.
const mapDispatchToProps = dispatch => ({
toggleCartHidden: () => dispatch(toggleCartHidden())
});
export default connect(mapStateToProps, mapDispatchToProps)(CartIcon);
import { connect } from "react-redux";
import { setCurrentUser } from "./redux/user/user.actions";
class ComponentName extends React.Component {
constructor(props){
super(props);
this.state = {
currentUser: ""
}
}
...
}
// Sets local component state of currentUser
// to equal user.currentUser in redux global state
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser
});
// Whenever local props is changed using setCurrentUser
// Dispatch the plain object output of the setCurrentUser()
// method from user.reducer.js to overwrite redux global state
const mapDispatchToProps = dispatch => ({
setCurrentUser: user => dispatch(setCurrentUser(user))
});
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);