Skip to content

Instantly share code, notes, and snippets.

@nadrane
Last active October 8, 2019 07:27
Show Gist options
  • Save nadrane/5221c64c421efe421bda9fdaab167dc2 to your computer and use it in GitHub Desktop.
Save nadrane/5221c64c421efe421bda9fdaab167dc2 to your computer and use it in GitHub Desktop.
Create your own react-redux connect
import React, { Component } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import { createStore } from "redux";
const shallowEqual = require("shallow-equal/objects");
const store = createStore((oldState={}, action) => {
return Object.assign(oldState, { storeValue: action.storeValue })
});
function connect(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
return class WrapperComponent extends Component {
constructor() {
super();
this.state = { storeState: store.getState() };
}
shouldComponentUpdate() {
// If the props to WrapperComponent do not change
// between setState calls, then we don't need to re-render.
// This operation below is known as a shallow comparison
const newProps = mapStateToProps(this.state.storeState, this.props);
return !shallowEqual(newProps, this.oldProps);
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState({ storeState: store.getState() });
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
this.oldProps = mapStateToProps(this.state.storeState, this.props)
const newProps = Object.assign(
{},
this.oldProps,
mapDispatchToProps(store.dispatch.bind(this))
);
return <WrappedComponent {...newProps} />;
}
};
};
}
const App = ({storeValue}) => {
console.log("App is re-rendering");
return (
<div>
<h2>Store Value {storeValue}</h2>
</div>
);
};
const NewApp = connect(
state => ({
storeValue: state.storeValue
}),
() => ({})
)(App);
class AppContainer extends Component {
constructor() {
super();
this.state = { counter: 0 };
}
//I suppose I could have used mapDispatch to props ¯\_(ツ)_/¯
changeStore(e) {
store.dispatch({type: "CHANGE", storeValue: `${e.target.value}`})
}
changeCounter() {
this.setState(oldState => ({ counter: ++oldState.counter }));
}
render() {
console.log("App Container is re-rendering")
return (
<div>
{/*Changing the store will cause a re-render. Check the console to prove this to yourself.
Notice the store only update when leaving the input box's focus */}
<label>update store state</label> <input onBlur={this.changeStore.bind(this)} />
{/*Notice that changing the parent of App never causes a re-render. Check the console to
prove this to yourself. */}
<button onClick={this.changeCounter.bind(this)}>update AppContainer state</button>
<span>{this.state.counter}</span>
<NewApp {...this.props} />
</div>
);
}
}
render(<AppContainer />, document.getElementById("root"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment