chobo2 chobo2 - 3 months ago 18
React JSX Question

How to hookup input boxes to Reactjs / Redux?

I not 100% sure if I am doing this right as per the redux design.

import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import 'materialize-css/sass/materialize.scss';
import NavigationContainer from './NavigationContainer';

import AddStorageModal from './AddStorageModal.js'
import {loadAddStorageModal, createNewStorage} from '../actions/StorageActions.js'
import '../styles/main.scss';


class App extends React.Component {
render() {
return (
<div>
<NavigationContainer />
<AddStorageModal {...this.props} />
</div>
)
}
}

function mapStateToProps(state) {
return {
storages: state.storages
};
}

function matchDispatchToProps(dispatch){
return bindActionCreators({loadAddStorageModal: loadAddStorageModal, createNewStorage: createNewStorage}, dispatch);
}


export default connect(mapStateToProps, matchDispatchToProps)(App);


StorageActions

export function fetchStorage() {
return function(dispatch) {
var payload = [
{
id: 1,
name: "Fridge2"
},
{
id: 2,
name: "Closet2"
},
{
id: 3,
name: "Car2"
}
];
dispatch({type: "Fetch_Storage", payload: payload});
}
}

export function loadAddStorageModal(load) {
return function(dispatch) {
dispatch({type: "Load_Add_Storage_Modal", payload: load});
}
}

export function createNewStorage(storage) {
return function(dispatch) {
dispatch({type: "New_Storage_Created", payload: storage});
}
}


Reducer

export default function reducer(state = {
fetchedStorages: [],
openAddStorageModal: false
}, action) {
switch (action.type) {
case "Fetch_Storage": {
return {
fetchedStorages: action.payload
}
}
case "Load_Add_Storage_Modal": {
return {
openAddStorageModal: action.payload,
fetchedStorages: state.fetchedStorages
}
}
case "New_Storage_Created": {
return {
openAddStorageModal: false,
}
}
}


return state;
}


AddStorageModal

import React from 'react';

import 'materialize-css/sass/materialize.scss';
import 'materialize-css/js/materialize.js';
import 'font-awesome/scss/font-awesome.scss';
import '../styles/main.scss';

export default class AddStorageModal extends React.Component {
constructor() {
super();
this.state = {storageName: ""}
}
handleChange(event) {
this.setState({storageName: event.target.value});
}
render() {
if (this.props.storages.openAddStorageModal) {
$('#add-new-storage-modal').openModal({ dismissible: false });
}
return (
<div id="add-new-storage-modal" className="modal" >
<div className="modal-content">
<h6>Enter your new Storage (Freezer, Pantry, etc.) </h6>
<div className="row">
<form>
<div className="input-field col s12 m12 l12 ">
<input id="storage_name" type="text" className="validate" value={this.state.storageName} onChange={this.handleChange} />
<label htmlFor="storage_name">Storage Name</label>
</div>
<br />
<h4 className="center">OR</h4>
<h6>Enter in the sharing key you were given.</h6>
<div className="input-field col s12 m12 l12 ">
<input id="sharing_key" type="text" className="validate" />
<label htmlFor="sharing_key">Sharking Key</label>
</div>
</form>
</div>
</div>
<div className="modal-footer">
<a href="#!" className="waves-effect waves-green btn-flat left">Add</a>
<a href="#!" className="modal-action modal-close waves-effect waves-green btn-flat" onClick={() => this.props.loadAddStorageModal(false) }>Cancel</a>
</div>
</div>
)
}
}


I get

Uncaught TypeError: Cannot read property 'setState' of undefined


So I am not sure if this just means I am doing redux wrong or if I just made some general error.

Answer

You can't pass a generic function reference, you need to keep the reference to this. You have 2 options:

  1. bind this to the function, like @nuway said in his answer.

  2. Use an arrow function, which also keeps the this reference: onChange={ (event) => this.handleChange(event) }

Comments