Where to store text input state with presentational and container components?

Viewed 588

Using the presentational and container components that Dan Abramov has previously discussed , I have a presentational component which has an input and button and a container component which handles the text given from the input and dispatches an event when the select button is pressed.

Presentational Component:

import React from 'react'

const NavBar = ({ onClick, handleChange }) => {
    return (
        <div className="NavBar">         
            <input onChange={handleChange} />
            <button type="submit" onClick={onClick}>Search</button>
        </div>
    )
}

export default NavBar

Container Component:

import {connect} from 'react-redux'
import NavBar from '../components/NavBar'
import {requestStat} from '../../stats/actions/StatsAction'

// meh, not a big fan of this, but I think it's the right compromise for handling the textinput.
let textInput

const mapDispatchToProps = (dispatch) => ({
    handleChange: (e) => textInput = e.target.value,
    onClick : (e) => handleSelectedTab(dispatch)    
})

const handleSelectedTab = (dispatch) => {
    if (textInput != null && textInput != "") {
        dispatch(requestStat(textInput))
    }
}

const NavBarContainer = connect(null, mapDispatchToProps)(NavBar)

I'm wondering where it's best to store the input string?

In my current solution I'm storing this state as in the textInput variable on the container component - is this the best place for it with this current set-up?

I've previously stored this value as a variable in the presentational component, which then passed the value to the container when submit was pressed, but this didn't feel quite right. The advantage that I get with storing the input value in the container is that the presentational component makes no assumptions of what to do with the input text, other than to run the handleChange callback and having this callback be injected into the presentational component makes for a natural unit test on this behaviour:

export default NavBarContainer

import React from 'react'
import {shallow} from 'enzyme'
import Navbar from './Navbar'


describe('NavBar', () =>{

    it ('run callback when input changed, setting value to true', () => {

        let change = false
        const handleChange = () => { change = true; }

        const props = { handleChange }
        const component = shallow(<Navbar {...props} />)

        expect(change).toBe(false)

        const formGroup = component.find('input')
        formGroup.simulate('change')

        expect(change).toBe(true)
    })
})

Any thoughts / best practises or advice would be much appreciated!

EDIT:

To clarify; I already have a redux store and actions in place, but it seems overkill to be updating the store every time someone types a character into the input field.

I also don't need to store this value once the user hits submit, as the event gets picked up by a saga, which then in turn uses it to get data from a web server, the results of which go into the redux store via a reducer.

Edit 2:

I'd also rather not use the component state as updating it will cause re-rendering and with its async nature I can't easily trust that it's been updated by the time the user hits the button.

3 Answers
Related