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.
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.
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With