Introduction: Simple implementation of react-redux basic api
react-redux api review
<Provider store>
Place the store in context, and all the subcomponents can get the store data directly.
Make Redux store available to connect() methods at the component level The root component should be nested in <Provider>.
ReactDOM.render( <Provider store={store}> <MyRootComponent /> </Provider>, rootEl ) ReactDOM.render( <Provider store={store}> <Router history={history}> <Route path="/" component={App}> <Route path="foo" component={Foo}/> <Route path="bar" component={Bar}/> </Route> </Router> </Provider>, document.getElementById('root') )
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Linking components and data, putting data in redux into the properties of components
[mapStateToProps(state, [ownProps]): stateProps] (Function)
If this parameter is defined, the component will listen for changes in the Redux store. Whenever the Redux store changes, the mapStateToProps function is called. The callback function must return a pure object that is merged with the props of the component.
If you omit this parameter, your component will not listen to Redux store
ownProps, the value of this parameter is passed to the component's props, and as long as the component receives the new props, mapStateToProps will also be called and recalculated.
The first parameter of the mapStateToProps function is the state of the entire Redux store, which returns an object to be passed as a props. It is usually called selector. reselect can be used to effectively combine selectors and compute derived data.
Note: If a mandatory parameter function is defined (the length of this function is 1), ownProps will not be passed to mapStateToProps.
const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } }
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function)
Object: It also corresponds to each key name. UI The key value of the same name parameter of the component should be a function. action creator),Will be treated as Action creator ,Returned Action Will be Redux Automatic issue, Function: Will get dispatch and ownProps(Container Component props Object) Two parameters (which may be used at this point) Redux Auxiliary function bindActionCreators())
This map DispatchToProps parameter is omitted. By default, dispatch is injected into your component props. You can call this.props.dispatch.
The second parameter ownProps in the callback function is specified, whose value is passed to the component's props, and mapDispatchToProps is also called as long as the component receives the new props.
eg:
connect(mapStateToProps, { hideAdPanel, pushAdData, })(AdPanel) function mapDispatchToProps(dispatch) { return { todoActions: bindActionCreators(todoActionCreators, dispatch), counterActions: bindActionCreators(counterActionCreators, dispatch) } }
Knowledge Point Supplement - React Higher-Order Components
A higher-order component is a function that accepts a component as a parameter and returns a new component.
High-order components are pure functions without side effects.
Use scenarios: Most of the code for both components is duplicated + and more closed, without concern for data acquisition
import React, {Component} from 'react' class Welcome extends Component { constructor(props) { super(props); this.state = { username: '' } } componentWillMount() { let username = localStorage.getItem('username'); this.setState({ username: username }) } render() { return ( <div>welcome {this.state.username}</div> ) } } export default Welcome; import React, {Component} from 'react' class Goodbye extends Component { constructor(props) { super(props); this.state = { username: '' } } componentWillMount() { let username = localStorage.getItem('username'); this.setState({ username: username }) } render() { return ( <div>goodbye {this.state.username}</div> ) } } export default Goodbye;
welcome and goodbye components are similar, but they can only get different data. Use high-level components to extract common parts.
import React, {Component} from 'react' export default (WrappedComponent) => { class NewComponent extends Component { constructor() { super(); this.state = { username: '' } } componentWillMount() { let username = localStorage.getItem('username'); this.setState({ username: username }) } render() { return <WrappedComponent username={this.state.username}/> } } return NewComponent } //Simplify welcome and goodbye import React, {Component} from 'react'; import wrapWithUsername from 'wrapWithUsername'; class Welcome extends Component { render() { return ( <div>welcome {this.props.username}</div> ) } } Welcome = wrapWithUsername(Welcome); export default Welcome;
At this point, it's easy to understand the connect ion of react-redux
ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component); // connect is a function of a return function (that is, a higher-order function) const enhance = connect(mapStateToProps, mapDispatchToProps); // The function returned is a higher-order component that returns a function with Redux store // New components associated const ConnectedComment = enhance(Component);
provider implementation
import React from 'react' import ReactDOM from 'react-dom' import { createStore, applyMiddleware, compose} from 'redux' import thunk from 'redux-thunk' import { counter } from './index.redux' // import { Provider } from 'react-redux' // Change to your own Provider implementation import { Provider } from './self-react-redux' import App from './App' const store = createStore(counter, compose( applyMiddleware(thunk), window.devToolsExtension ? window.devToolsExtension() : f => f )) ReactDOM.render( ( <Provider store={store}> <App /> </Provider> ), document.getElementById('root'))
./self-react-redux
import React from 'react' import PropTypes from 'prop-types' export function connect(){ } class Provider extends React.Component{ static childContextTypes = { store: PropTypes.object } getChildContext() { return { store: this.store } } constructor(props, context) { super(props, context) this.store = props.store } render(){ return this.props.children } }
connect implementation
demo
import React from 'react' // import { connect } from 'react-redux' import { connect } from './self-react-redux' import { addGun, removeGun, addGunAsync } from './index.redux' @connect( // What attributes do you want state to put in props state=>({num:state.counter}), // What method do you want to put it in props and dispatch automatically? { addGun, removeGun, addGunAsync } ) class App extends React.Component{ render(){ return ( <div> <h1>Now organic guns{this.props.num}hold</h1> <button onClick={this.props.addGun}>Application for weapons</button> <button onClick={this.props.removeGun}>Hand over weapon</button> <button onClick={this.props.addGunAsync}>Two days later</button> </div> ) } } export default App
./self-react-redux.js
// Writing of Higher Order Components export function connect(maoStateToProps, mapStateToProps) { return function(WrapComponent) { return class ConnectComponent extends React.Component{ } } } import React from 'react' import PropTypes from 'prop-types' import { bindActionCreator } from './self-redux' // Use abbreviations // connect is responsible for linking components and putting data to redux in the properties of components // 1. Responsible for receiving a component, putting some data in the state and returning a component // 2. Notify components when data changes export const connect = ( mapStateToProps = state => state, mapDispatchToProps ={} ) => (WrapComponent) => { return class ConnectComponent extends React.Component { static contextTypes = { store: PropTypes.object } constructor(props, context){ super(props, context) this.state = { props: {} } } // 2. Implement mapStateToProps componentDidMount() { const { store } = this.context store.subscribe(() => this.update()) this.update() } update() { const { store } = this.context // store.getState() That's why you get the state in the mapStateToProps function const stateProps = mapStateToProps(store.getState()) // The method cannot be given directly because dispatch is required. /** function addGun() { return { type: ADD_GUN } } It makes no sense to directly execute addGun(). It makes sense to add Gun = () = > store. dispatch (addGun ()) by wrapping actionCreator in one layer. bindActionCreators Implementation of handwritten redux api */ const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch) // Note that the sequential problem of state overrides this.setState({ props: { ...this.state.props, ...stateProps, ...dispatchProps, } }) } // 1 render() { return <WrapComponent {...this.state.props}></WrapComponent> } } }
./self-redux.js
// creators: {addGun, removeGun, addGunAsync} // creators[v]: addGun (parameter) // Return: (parameter) => dispatch (addGun (parameter)) function bindActionCreator(creator, dispatch) { return (...args) => dispatch(creator(...args)) } export function bindActionCreators(creators, dispatch) { let bound = {} Object.keys(creators).forEach( v => { let creator = creators[v] bound[v] = bindActionCreator(creator, dispatch) }) return bound } // Abbreviation export function bindActionCreators(creators, dispatch) { return Object.keys(creators).reduce((ret, item) => { ret[item] = bindActionCreator(creators[item], dispatch) return ret }, {}) }