Search code examples
javascriptnode.jsreactjsreduxsocketcluster

Redux actions get triggered unnecessarily on state change


I'm trying to integrate socketCluster with a simple redux app. Currently , there is only one feature which takes the value of the current state in a component and sends it to the server through a middleware.

this is my actions.js file

export function signupRequest(creds){
  return{
    type: SIGNUP_REQUEST,
    isFetching: true,
    isAuthenticated: false,
    creds
  }
}

my reducers:

function signUp(state={
  isFetching:false,
  isAuthenticated: localStorage.getItem('id_token')? true : false
},action)
{
  switch(action.type){
    case SIGNUP_REQUEST:
      return Object.assign({},state,{
        isFetching:true,
        isAuthenticated: false,
        user: action.creds
      })
}

i have a middleware function which is responsible for emitting the input to the server

middleware.js

export default socket => store => next => action => {
  socket.emit('action',action);
}

my client side index.js file

const socket = connect();
const createStoreWithMiddleware = applyMiddleware(middlewares(socket))(createStore);
const store = createStoreWithMiddleware(reducer);

let rootElement = document.getElementById('root')

render(
  <Provider store={store}>
    <App/>
  </Provider>,rootElement
)

my App.jsx container :

class App extends Component{

  render(){
      const {dispatch,isAuthenticated,errorMessage,isFetching} = this.props;
    return(
      <Signup
        onButtonClick = {this.props.signupRequest}/>
    )
  }
}

function mapStateToProps(state){
  const {isAuthenticated,errorMessage,isFetching} = state.signUp

  return {
    isAuthenticated,
    errorMessage,
    isFetching
  }
}

function mapDispatchToProps(dispatch){
  return bindActionCreators({signupRequest:signupRequest},dispatch)
}

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

my Signup Component:

class Signup extends Component{

  constructor(props){
    super(props);

    this.state = {name:''};
    this.onInputChange = this.onInputChange.bind(this)
  }

  onInputChange(event){
    this.setState({name:event.target.value})
  }

  render(){
    return (
      <div>
        <input type="text"
          ref="username"
          className="SignupUsername"
          placeholder="Username"
          value = {this.state.name}
          onChange={this.onInputChange}
          />
        <button onClick= {this.props.onButtonClick(this.state.name)} >
          Signup
        </button>
      </div>
    )
  }
}

export default Signup

the problem i'm facing is that the middleware/actions get triggered on every keypress in the input. I would like to invoke the middleware only on the button Click event.

this is the output i'm getting on the server side

action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'a' }
action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'as' }
action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'asd' }
action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'asda' }
action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'asdas' }
action recieved : { type: 'SIGNUP_REQUEST',
  isFetching: true,
  isAuthenticated: false,
  creds: 'asdasd' }

as you can see , the socket is sending the action on every keypress in the input. Clicking on the button does not invoke the middleware either

EDIT - i tried converting the signup component as a container - but i still seem to be having the same problem

class Signup extends Component{

  constructor(props){
    super(props);

    this.state = {name:''};
    this.onInputChange = this.onInputChange.bind(this)
  }

  onInputChange(event){
    this.setState({name:event.target.value})
  }

  render(){
    return (
      <div>
        <input type="text"
          ref="username"
          className="SignupUsername"
          placeholder="Username"
          value = {this.state.name}
          onChange={this.onInputChange}
          />
        <button onClick= {this.props.signupRequest(this.state.name)} >
          Signup
        </button>
      </div>
    )
  }
}

function mapDispatchToProps(dispatch){
  return bindActionCreators({signupRequest:signupRequest},dispatch)
}

export default connect(null,mapDispatchToProps)(Signup)

Solution

  • Turns out that the problem was on the onClick event - i converted the onClick event into a function as below:

    <button onClick= {() => this.props.signupRequest(this.state.name)} >
      Signup
    </button>
    

    as Opposed to

      <button onClick= {this.props.signupRequest(this.state.name)} >
              Signup
            </button>