How to handle action in Reducer?

2019-06-10 04:04发布

问题:

How do I target which element/object in array in a reducer I have to pass the action to? I am trying to figure out what action I take on the reducer. Or how do I change affect the number value in the object. So I am rendering the array in a View with TouchableOpacity which onPress Im calling the action dispatch as following:

import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';

class NewData extends Component {
    render(){
    const renData = this.props.newData.map((data, idx) => {
        return (
            <View key={idx}>
              <TouchableOpacity
                onPress={() => this.props.incNum(data.id)}
                //not sure how to toggel dispatch call above, for onPress
              >
              <Text style={styles.welcome}>{data.id} - {data.name} - {data.number}</Text>
              </TouchableOpacity>
            </View> 
        )
    });

        return(
            <View>
          {renData}
      </View>   
        );
    }
}


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


//I think I need to create a toggle here:
//Something like: this.props.newData.id ? incNum : decNum


function mapDispatchToProps (dispatch) {
  return {
    incNum: (id) => {
      dispatch({
        type: "INC_NUM",
        payload: id
      })
    },
     decNum: (id) => {
      dispatch({
        type: "DEC_NUM",
        payload: id
      })
    }
  };
};  

export default connect( mapStateToProps, mapDispatchToProps )( NewData )

My Reducer:

const initialState = [
      {
        id: '01',
        name: 'Name One',
        number: 11
      },
      {
        id: '02',
        name: 'Name Two',
        number: 22
      },
      {
        id: '03',
        name: 'Name Three',
        number: 33
      }

    ]

export default function newData (state = initialState, action) {
  switch (action.type) {
    case "INC_NUM":
      return {
        ...state,
        number: this.state.number ++ // <== need help here.
      }
    case "DEC_NUM":
      return {
        ...state,
        number: this.state.number --
      }
    default:
      return state
  }
}

I think <TouchableOpacity onPress={() => this.props.incNum(data.id)} will bind the id to be changed. And passing the payload: id will pass the id to the reducer. But how do I update the value in the reducer? I can do without the toggle for now. I want to learn about passing the value and updating accordingly in the reduder.

Thank you.


EDIT1 :

So my reducer now is:

const initialState = {
  1: {
     id: '01',
     name: 'Name One',
     number: 11
    },
  2: {
     id: '02',
     name: 'Name Two',
     number: 22
    }
}


export default function newData (state = initialState, action) {
  switch (action.type) {
    case "INC_NUM":
      return {
        ...state,
        [action.payload]: {
          ...state[action.payload], 
          number: state[action.payload].number++  <== Line 58 in ERROR screenshot
        }
    case "DEC_NUM":
        return {
        ...state,
        [action.payload]: {
          ...state[action.payload], 
          number: state[action.payload].number--
        }
    default:
      return state
  }
}

And the way I'm rendering it is:

class NewData extends Component {
  render(){

    const renData = Object.keys(this.props.newData).map((key, idx) => {
      let data = this.props.newData[key]
        return (
        <View key={idx}>
          <TouchableOpacity

            onPress={() => this.props.updateNum(data.id)}
          >
          <Text style={styles.welcome}>{data.id} - {data.name} - {data.number}</Text>
          </TouchableOpacity>
        </View> 
       )
    });

    return(
        <View>
            {renData}
        </View>   
    )
  }
}


function mapDispatchToProps (dispatch) {
  return {
    updateNum: (id) => {
      INC_NUM({
        type: "INC_NUM",
        payload: id
      })
    }
  };
};

Everything in the reducer is appearing as expected. But when I click and the action is called, I get an error:

回答1:

your reducer seems to be lot more complex that it needs to be, below is the simpler approach, hope this helps.

const initialState = {
  1: {
     id: '01',
     name: 'Name One',
     number: 11
    },
  2: {
     id: '02',
     name: 'Name Two',
     number: 22
    }
}


    export default function newData (state = initialState, action) {
      switch (action.type) {
        case "INC_NUM":
             const newState = {...state};
             newState[action.payload].nubmer++;
             return newState;

        case "DEC_NUM":
            const newState = {...state};
            newState[action.payload].nubmer--;
            return newState;

        default:
          return state
      }
    }


回答2:

Your problem appears to be in your reducer and the structure of your state.. you're not using the ID to identify which keyval pair to update.

First I'd modify your initialState to be an object and use the id as your keys:

const initialState = {
  1: {name: 'Name One', number: 11},
  2: {name: 'Name Two', number: 22},
  3: {name: 'Name Three', number: 33}
}

Then in your reducer:

case "INC_NUM":
  return {
    ...state,
    [action.id]: {
      ...state[action.id], 
      number: state[action.id].number++
    }
  }

I know the syntax is convoluted, but the spread operator is nice shorthand for what otherwise would be even harder to read.