Last active
October 8, 2019 07:27
-
-
Save nadrane/5221c64c421efe421bda9fdaab167dc2 to your computer and use it in GitHub Desktop.
Create your own react-redux connect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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