1
votes

Update: 12/23/2019

I've updated Screen.js to incorporate both @Will Jenkins' and @Huỳnh Tuân 's suggestions, but it is still not working.

In addition, I've added 2 lines of debug code in the hopes of making the issue clearer.


I am new to React and brand new to Redux - and am trying to learn how to use Redux to control the state of my app by following the basic tutorial (which feels more advanced than basic to me). I have also looked at this tutorial and this tutorial on redux, as well as simliar posts here on SO, but so far to no avail.

My project 2 components; a text component to show a message obtained from an external API, and a button that, upon tap, gets data from the API and re-render the message. That's it!

However, when I tap on the button, nothing happens despite following the tutorial as closely as possible. I did wrap my root container in a <Provider>, and connect()ed my presentational components to the store. Yet somehow it's still not working.

Clearly, something is not right here. Can someone point out to me exactly what is it that I am doing wrong? Below is my code (imports are omitted for conciseness).

App.js

export default function App() {
    return (
        <Provider store={store}>
            <Screen />
        </Provider>
    )
}

Screen.js

class Screen extends React.Component {

    constructor(props) {
        super(props)
        //this.state = {question: ''}
        this.state = {reducer: { question } }
    }

    getDataFromAPI() {
        fetch('https://opentdb.com/api.php?amount=5&type=boolean')
        .then((response) => response.json())
        .then((responseJson) => {
            console.log("before", this.props.question) // undefined
          this.props.updateQuestion(responseJson.results[0].question)
            console.log("after", this.props.question) // undefined
        })
        .catch((error) => {
            console.error(error);
        });
    }

    render() {
        return (
            <View style={{flex: 1, backgroundColor:'fff', alignItems:'center', justifyContent:'center'}}>
                <Text> {this.props.question} </Text>
                <Button title='New Trivia' onPress={() => this.getDataFromAPI()} />
            </View>
        );
    }

}

{/*const mapStateToProps = (state) => {
    return {
        question: state.question
    }
}*/}
const mapStateToProps = ({ reducer: { question } }) => {
    question
}

const mapDispatchToProps = dispatch => ({
    updateQuestion: (question) => dispatch(updateQuestion(question))
})

export default connect(mapStateToProps, mapDispatchToProps)(Screen)

Actions.js

export const updateQuestion = (response) => ({
    type: UPDATE_QUESTION,
    question: response,
})

ActionTypes.js

export const UPDATE_QUESTION = "UPDATE_QUESTION"

store.js

export default createStore(rootReducer)

index.js

export default combineReducers({reducer})

Reducer.js

const initialState = { question: '' }

export default function (state = initialState, action) {
    switch(action.type) {
        case UPDATE_QUESTION: {
            return {...state, question: action.question}
        }
        default: {
            return state
        }
    }
}
2
If your mapStateToProps is actually defined as stated here, you aren't returning anything, so no props are being mapped. Try wrapping the { question } in parenthesis. So mapState.. would be = (...blabla) => ({question}) - tcanusso

2 Answers

1
votes

The correction by Will Jenkins is great, but he missed wrapping the object in parenthesis when returning from mapStateToProps. This returns undefined because it's not returning anything. Either explicitly write return { question } or define mapState as

const mapStateToProps = ({ reducer: { question } }) => ({ // <-- Notice the parenthesis
  question
}) // <-- Notice the parenthesis

By the way, not to be mean or anything, try solving this kind of errors by your own. It's common to have mistakes in our code, and it's the best opportunity to learn. I would recommend to try and solve certain errors (like something returns undefined, or is not what you expected, or a function you were calling isn't doing what it's supposed to) by yourself, just because that way you're going to learn how to solve problems, and be more solid as a JS programmer.

0
votes

You need to import your Action that you want to call then dispatch it for calling Action Redux in components. Change your Screen.js to:

import { updateQuestion } from './Actions'
class Screen extends React.Component {

  constructor(props) {
      super(props)
      this.state = {question: ''}
  }

  getDataFromAPI() {
      const { updateQuestion } = this.props;
      fetch('https://opentdb.com/api.php?amount=5&type=boolean')
      .then((response) => response.json())
      .then((responseJson) => {
        updateQuestion(responseJson.results[0].question)
      })
      .catch((error) => {
          console.error(error);
      });
  }

  render() {
      return (
          <View style={{flex: 1, backgroundColor:'fff', alignItems:'center', justifyContent:'center'}}>
              <Text> {this.props.question} </Text>
              <Button title='New Trivia' onPress={() => this.getDataFromAPI()} />
          </View>
      );
  }

}

const mapStateToProps = (state) => {
  return {
      question: state.question
  }
}

const mapDispatchToProps = dispatch => ({
  updateQuestion: (question) => dispatch(updateQuestion(question)) 
})
export default connect(mapStateToProps, mapDispatchToProps)(Screen)