Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Как исправить ошибку в своей реализации redux? (https://javascript.ru/forum/dom-window/78217-kak-ispravit-oshibku-v-svoejj-realizacii-redux.html)

gsdev99 11.08.2019 07:26

Как исправить ошибку в своей реализации redux?
 
Всем привет. У меня не есть самостоятельная реализация redux (разбираюсь с чужим кодом). И у меня возникла следующая проблема.

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')
)


В данном примере очевидная проблема, что не передается initialState
Что сделал я: в reducer дописал: state = initialState, ну и конечно описал 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
  }
}


Остались две проблемы:
- initialState нет при инициализации компонента
- И когда reducer обновляет состояние, не происходит render IntervalComponent, соответственно визуально ничего не меняется.
Буду благодарен любой помощи.

https://codepen.io/gsdev99/pen/pYqmRr

SuperZen 13.08.2019 08:04

const initialState =  {
currentInterval:  3000
}

const createStore = (reducer, initialState) => {
  let currentState = initialState
  const listeners = []
  
  console.log('currentState', currentState)
  

  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)}
          />
        )
      }
      
      componentDidMount() {
        console.log('WRAPPEDCOMPONENT DID MOUNT')
        this.context.store.subscribe(this.handleChange)
      }

//      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() {
    console.log('getChildContext, store', this.props.store)
    return {
      store: this.props.store,
    }
    
  }

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

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


// actions

const FETCH_INITIAL_STATE = 'FETCH_INITIAL_STATE'
const CHANGE_INTERVAL = 'CHANGE_INTERVAL'

// action creators
const fetchInitialState = () => ({
  type: FETCH_INITIAL_STATE
})

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

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

  switch (action.type) {

    case FETCH_INITIAL_STATE:
      return state

    case CHANGE_INTERVAL:
      
      console.log('CHANGE_INTERVAL', state, action)
      
      const nextState = {
        ...state,
        currentInterval: state.currentInterval + action.payload,
      }
      
      console.log('nextState', nextState, 'ok')
      
      return nextState

    default:
      return state
  }
}

// components

class IntervalComponent extends React.Component {

  componentDidMount() {
    console.log('componentDidMount()')
    this.props.fetchInitialState()

    console.log('props', this.props)
  }

  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,
    // currentInterval: state.currentInterval,
  }),
  dispatch => ({
    changeInterval: value => dispatch(changeInterval(value)),
    fetchInitialState: value => dispatch(fetchInitialState()),
  }))(IntervalComponent)

const str = createStore(reducer, initialState)

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


) что-то получилось...

1) inititalState ты не правильно передавал, тк createStore уже ждет initialState
2) потом неправильный объект был
3) и напоследок, не там стоял подписчик...

gsdev99 13.08.2019 17:34

Спасибо тебе большое!


Часовой пояс GMT +3, время: 20:09.