1
votes

I have seen similar questions but they don't seem to help. I have a React project I am implementing redux on.

My index.js is as follows:

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";

import reducer from "./store/reducer";

import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

const store = createStore(reducer);

const app = (
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
);

ReactDOM.render(app, document.getElementById("root"));

serviceWorker.unregister();

My reducer.js is like so:

import * as actionTypes from "./actions";

const initialState = {
  searchValue: "",
  finalSearchValue: ""
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case actionTypes.UPDATE_INPUT_VALUE:
      return {
        searchValue: state.searchValue
      };
    case actionTypes.SUBMIT_QUERY:
      return {
        finalSearchValue: state.finalSearchValue
      };
    default:
      return state;
  }
};

export default reducer;

And my Songfinder.js - where I'm trying to mapStateToProps and mapDispatchToProps is like this:

import React, { Component } from "react";
import { connect } from "react-redux";
import PanelSearchResults from "../../components/PanelSearchResults/PanelSearchResults";
import Playlist from "../Playlist/Playlist";

import classes from "./SongFinder.css";
import Auxilliary from "../../hoc/Auxilliary";
import * as actionTypes from "../../store/actions";

class SongFinder extends Component {
  props: {
    searchValue: string;
    finalSearchValue: string;
  };

  componentDidMount() {
    console.log(this.props);
  }

  updateInputValue(evt) {
    this.props.onUpdateSearchValue(evt.target.value);
  }

  onSubmitQueryHandler() {}

  render() {
    if (this.props) {
      return (
        <Auxilliary>
          <div className={classes.panel}>
            <input
              className="searchBox"
              type="text"
              value={this.props.searchValue}
              onChange={evt => this.updateInputValue(evt)}
            />
            <button type="submit" onClick={this.onSubmitQueryHandler}>
              Submit
            </button>
          </div>
          <div className={classes.panel}>
            <h1>Playlist</h1>
            <Playlist />
          </div>
          <PanelSearchResults search={this.props.finalSearchValue} />
        </Auxilliary>
      );
    } else {
      return null;
    }
  }
}

const mapStateToProps = state => {
  return {
    searchValue: state.searchValue,
    finalSearchValue: state.finalSearchValue
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onUpdateSearchValue: value =>
      dispatch({
        type: actionTypes.UPDATE_INPUT_VALUE,
        searchValue: value
      }),
    onSubmitQuery: value =>
      dispatch({
        type: actionTypes.SUBMIT_QUERY,
        finalSearchValue: value
      })
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(SongFinder);

Every time the input text is changed, I want the state to be updated via mapDispatchToProps, and I want the onChange event to trigger the updateInputValue handler, which should trigger the onUpdateSearchValue in mapDispatchToProps, sending it the evt.target.value.

Tearing my hair out trying to figure out why it doesn't work. I'm in React 16.6.3 - is it a Typescript error? Both values in my initialState declared in the reducer are logging to the console via componentDidMount() in Songfinder.js so props are available to this component?

1

1 Answers

2
votes

Looks like your problem is that the reducer isn't actually creating any new state because you're referring to state instead of action when deriving the new state. The state parameter contains the previous state from the last time the reducer ran, while action contains the info you need to derive a new state this time around. Like so:

import * as actionTypes from "./actions";

const initialState = {
  searchValue: "",
  finalSearchValue: ""
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case actionTypes.UPDATE_INPUT_VALUE:
      return {
        searchValue: action.searchValue,
        finalSearchValue: ""
      };
    case actionTypes.SUBMIT_QUERY:
      return {
        searchValue: "",
        finalSearchValue: action.finalSearchValue
      };
    default:
      return state;
  }
};

export default reducer;

I also added extra fields in the returns above because a reducer should probably return a full state each time (same shape as initialState).