Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it against redux philosophy to copy redux state to local state?

Tags:

reactjs

redux

import React, { Component } from 'react'
import _ from 'lodash'
import { PageHeader, Row, Col, FormGroup, ControlLabel, Button, FormControl, Panel } from 'react-bootstrap'

import FieldGroup from '../fieldGroup/fieldGroup'
import PassagePreview from './preview'

class PassageDetail extends Component {
  constructor(props) {
    super(props)

    this.handleChange = this.handleChange.bind(this)

    this.state = {
      passageDetails: {}
    }

  }

  componentDidMount() {
    this.props.fetchPassage(this.props.params.passageId)
  }

  componentWillReceiveProps(nextProps) {
    if(nextProps.passageState.passageStatus === 'success') {
      this.setState({passageDetails: nextProps.passageState.passage})
    }
  }

  handleChange(e) {
    let passageDetails = this.state.passageDetails

    passageDetails[e.target.name] = e.target.value

    this.setState({passageDetails})
  }

  render() {
    if(this.props.passageState.passageStatus !== 'success') {
      return null
    }
    return (
      <div>
        <PageHeader>
          Create Passage
        </PageHeader>
        <Row>
          <Col md={6}>
            <FieldGroup
              type="text"
              label="Passage Title"
              name="title"
              onChange={this.handleChange}
              value={this.state.passageDetails.title}
            />

            <FieldGroup
              type="text"
              label="Passage Author"
            />
            <FormGroup>
              <ControlLabel>Passage Text</ControlLabel>
              <FormControl componentClass="textarea" placeholder="textarea" rows="20" />
            </FormGroup>
          </Col>
          <Col md={6}>
            <PassagePreview passageDetails={this.state.passageDetails} />
          </Col>
        </Row>

        <Row>
          <Col md={6} mdOffset={6}>
            <Button block bsStyle="primary">Create Passage</Button>
          </Col>
        </Row>
      </div>
    )
  }
}

export default PassageDetail

That's my component. The container is:

import { connect } from 'react-redux'
import * as PassagesActions from '../actions/passages'
import PassageMain from '../components/passage/main'

const mapStateToProps = (state) => {
  return {
    passageState: state.PassageReducer
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    fetchPassages: () => {
      const params = {
        url: `${process.env.REACT_APP_API_ENDPOINT}/passages`
      }
      dispatch(PassagesActions.fetchData(params, 'passages'))
    },
    fetchPassage: (passageId) => {
      const params = {
        url: `${process.env.REACT_APP_API_ENDPOINT}/passages/${passageId}`
      }
      dispatch(PassagesActions.fetchData(params, 'passage'))
    }
  }
}

export default connect(
  mapStateToProps, mapDispatchToProps
)(PassageMain)

So you can see that in my componentDidMount, I'm doing an API call (via redux) and in componentWillReceiveProps, I'm copying that to my local state. The idea being that if I make changes to my passage, it's done at the component level then I can pass that object back through some yet-to-be-created redux action.

Is this acceptable within redux philosophy? I'm still wrapping my head around redux, so I apologize if this question makes no sense.

like image 755
Shamoon Avatar asked Feb 28 '17 00:02

Shamoon


3 Answers

The other comments make some good points, but also overstate the case. Per the Redux FAQ question at http://redux.js.org/docs/faq/OrganizingState.html#organizing-state-only-redux-state :

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

Some common rules of thumb for determing what kind of data should be put into Redux:

  • Do other parts of the application care about this data?
  • Do you need to be able to create further derived data based on this original data?
  • Is the same data being used to drive multiple components?
  • Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
  • Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?

So, if you want to use local component state as the "temporary data" area before dispatching an action with the result, that's okay.

like image 183
markerikson Avatar answered Oct 14 '22 00:10

markerikson


It is against the "redux philosophy" for good reasons, you have an architecture and a framework made for storing and centralizing the state of your application, you should use it! If you use the component state you're decentralising the application state and sooner or later you're going to have a disaster. Stick to the Redux architecture as much as posible, use the component state only for minor UI details, or even better, don't use it at all.

It could help to take a look at your reducer, but it is evident that you don't need the component state if you're already holding the collection of passages in your Redux store.

If you already have basic information about a passage and you need to load more from the server for that component, all your passages should have a flag denoting whether you already have that information or not, so when your component loads you retrieve the information only if needed.

There is no need to use componentWillReceiveProps, just fire the action, update the store in the reducer when the action completes and that's it. Your collection will have the passage, you'll need to pass it to the component (all of it, not just the id).

Redux is a very low level framework, and it gives you a lot of freedom. If you doubt whether or not you're making the right decisions, always keep in mind:

  • Any information relevant to the application goes into the Redux store
  • Anything that affects the application in any way is an action
  • React components display information and fire redux actions with the information they collect from the user, nothing else

Another thing to note is that your component should receive only the properties it needs. You're passing it the entire reducer, that might be completely OK, but if you have a lot of data in the reducer that doesn't affect the component at all the performance will decrease as you'll force React to update the component when there is no need to do so.

like image 39
Marco Scabbiolo Avatar answered Oct 14 '22 01:10

Marco Scabbiolo


The more "Redux" way to do this is to dispatch an action in to the store to update the state and let redux re-render you're component. What you have here would not be considered good practise.

The issue you is that the components state will get out of sync with the store, so if the store gets updated and a re-render is triggered, the component may receive new props (possibly not if nothing else is updating the state.PassageReducer) and will override the local state.

Take a look at the redux docs on Unidirectional Data Flow and the todo example where they add new todos.

like image 25
Michael Peyper Avatar answered Oct 14 '22 01:10

Michael Peyper