G
G
gsdev992019-08-11 07:23:55
JavaScript
gsdev99, 2019-08-11 07:23:55

How to fix a bug in your redux implementation?

Hello. I don't have my own implementation of redux (I'm tinkering with someone else's code). And I got the following problem.

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

// Начну по порядку. Ниже приведены функции createStore, connect, Provider

const createStore = (reducer, initialState) => {
  let currentState = initialState
  const listeners = []

  const getState = () => currentState
  const dispatch = action => {
    currentState = reducer(currentState, action)
    listeners.forEach(listener => listener())
  }

  const subscribe = listener => listeners.push(listener)

  return { getState, dispatch, subscribe }
}

const connect = (mapStateToProps, mapDispatchToProps) =>
  Component => {
    class WrappedComponent extends React.Component {
      render() {
        return (
          <Component
            {...this.props}
            {...mapStateToProps(this.context.store.getState(), this.props)}
            {...mapDispatchToProps(this.context.store.dispatch, this.props)}
          />
        )
      }

      componentDidUpdate() {
        console.log('componentDidUpdate()')
        this.context.store.subscribe(this.handleChange)
      }

      handleChange = () => {
        console.log('handleChange')
        this.forceUpdate()
      }
    }

    WrappedComponent.contextTypes = {
      store: PropTypes.object,
    }

    return WrappedComponent
  }

class Provider extends React.Component {
  getChildContext() {
    return {
      store: this.props.store,
    }
  }

  render() {
    return React.Children.only(this.props.children)
  }
}

Provider.childContextTypes = {
  store: PropTypes.object,
}

// Ниже приведены actions, action creators, reducers

// actions
const CHANGE_INTERVAL = 'CHANGE_INTERVAL'

// action creators
const changeInterval = value => ({
  type: CHANGE_INTERVAL,
  payload: value,
})

// reducers
const reducer = (state, action) => {
  switch(action.type) {

    case CHANGE_INTERVAL:
      return {
        ...state,
        currentInterval: state.currentInterval + action.payload
      }

    default:
      return state
  }
}

// Далее компонент, которые будет отрендерен

class IntervalComponent extends React.Component {
  render() {
    console.log('render()')
    console.log(this.props)

    return (
      <div>
        <span>Интервал обновления секундомера: {this.props.currentInterval} сек.</span>
        <span>
          <button onClick={() => this.props.changeInterval(-1)}>-</button>
          <button onClick={() => this.props.changeInterval(1)}>+</button>
        </span>
      </div>
    )
  }
}

const Interval = connect((state) => ({
    currentInterval: state,
    // currentInterval: state.currentInterval,
  }),
  dispatch => ({
    changeInterval: value => dispatch(changeInterval(value))
  }))(IntervalComponent)


// init
ReactDOM.render(
  <Provider store={createStore(reducer)}>
    <Interval />
  </Provider>,
  document.getElementById('root')
)

In this example, the obvious problem is that initialState is not transmitted.
What I did: added to the reducer: state = initialState, and of course described initialState
const initialState = {
  currentInterval: 3000
}

// reducers
const reducer = (state = initialState, action) => {
  console.log('state', state)
  console.log('action', action)

  switch(action.type) {
    case CHANGE_INTERVAL:
      return {
        ...state,
        currentInterval: state.currentInterval + action.payload
      }

    default:
      return state
  }
}

There are two problems left:
- there is no initialState when the component is initialized
- And when the reducer updates the state, the render IntervalComponent does not occur, so nothing changes visually.
I will be grateful for any help.
https://codepen.io/gsdev99/pen/pYqmRr

Answer the question

In order to leave comments, you need to log in

1 answer(s)
B
bini1988, 2019-08-11
@bini1988

Redux dispatches the initial action the first time the store is created, passing undefined to the reducer and a special action {type: "INIT" }. The second point, I think the subscription to the store should be in componentDidMount, you can also add an unsubscribe from the store to componentWillUnmont to remove unnecessary cobacks.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question