Search code examples
javascriptformsreact-reduxactiondispatch

How to get Form submit value in react-redux with rails backend?


I have a Rails backend, with a list of ingredients. I am trying to use a Search Bar (form) to input an ingredient ID to find the appropriate ingredient. When I click the submit button of the form, I want the dispatch function getIngredient, with ID as argument, but with the approaches I have tried, I am not getting what is supposed to be the ID; it is undefined, or simply "an event". I only saw the error message briefly, so I don't have more details then that. It simply refreshes state, erasing the error messages.

I have tried having the value of the form onSubmit={() => getIngredient(id)}, it then returns as an event object. with onSubmit={() => this.props.getIngredient(id)} it returns undefined. Stating it simply as onSubmit={getIngredient(id)} also didn't work.

I have console.logged everything, and I found that it errors when it hits the dispatch action in getIngredient(id), because the id is undefined.

The getIngredients method I have works, it was when trying to only grab one ingredient I have had trouble. I have read everything I could find about redux, react-redux, and even reselect to try and target the issue, and I have hit this wall.

IngredientForm.js component:

        <form onSubmit={(id) => getIngredient(id)}>
          <label value="Find Ingredient">
            <input type="text" placeholder="Search By Id" />
          </label>
          <input type="submit" value="Find Ingredient" />
        </form>

const mapDispatchToProps = { getIngredient }

const mapStateToProps = (state, props) => {
  const id = props.id;
  return {
    ingredients: state.ingredients.filter(ingredient => ingredient.id === id)
  };
};

Dispatch action creator in actions.js:

export function getIngredient(id) {
  return dispatch => {
        console.log("trying to get id to show up =>", id)
    dispatch(getIngredientRequest(id));
    return fetch(`v1/ingredients`)
      .catch(error => console.log(error))
      .then(response => response.json())
      .then(json => dispatch(getIngredientSuccess(json)))
      .catch(error => console.log(error));
  }
}

action:

export function getIngredientRequest(id) {
  return { type: GET_INGREDIENT_REQUEST, id };
}

reducer.js

function rootReducer(state = initialState, action) {
  console.log(action.type);
  switch (action.type) {
    case "GET_INGREDIENTS_SUCCESS":
      return { ...state, ingredients: action.json.ingredients }
    case "GET_INGREDIENTS_REQUEST":
      console.log('Ingredients request received')
      return
    case "HIDE_INGREDIENTS":
      console.log('Ingredients are being hidden')
      return { ...state, ingredients: [] }
    case "GET_INGREDIENT_REQUEST":
      console.log('One Ingredient request received:', "id:", action.id)
      return
    case "GET_INGREDIENT_SUCCESS":
    console.log('GET_INGREDIENT_SUCCESS')
      return {
                ...state,
                ingredients: action.json.ingredients.filter(i => i.id)
            }
    default:
      return state
  }
}

Server terminal window output:

Processing by StaticController#index as HTML
  Parameters: {"page"=>"ingredients"}
  Rendering static/index.html.erb within layouts/application
  Rendered static/index.html.erb within layouts/application (0.9ms)
Completed 200 OK in 9ms (Views: 7.9ms | ActiveRecord: 0.0ms)```

Solution

  • That's because in React Synthetic Events - like onSubmit - the first parameter in the callback is always the Event object. I recommend you to use controlled inputs. That means you keep the id value in the component's state and assign that value to the input. Whenever the user types a new id, you update the state and assign the new value to the input.

    class Form extends React.Component {
     state = {
      id: ""
     };
    
     handleInputChange = event => {
      this.setState({ id: event.target.value });
     };
    
     render() {
      return (
       <form
        onSubmit={(event) => {
         event.preventDefault();
         getIngredient(this.state.id);
        }}
       >
        <label value="Find Ingredient">
         <input
          type="text"
          placeholder="Search By Id"
          onChange={this.handleInputChange}
          value={this.state.id}
         />
        </label>
        <input type="submit" value="Find Ingredient" />
       </form>
      );
     }
    }