1

I have a problem when trying to fetch initial data for my app from an api.

My problem is that after the console.log(url) in the action, nothing happens. I see the url in the console, but the rest of the code in getInitialRuns() doesn't seem to be executed, atleast not the way I expect. I get no error messages.

When using Postman, I can succesfully get a response from that API endpoint, so the API should be fine.

I have actions that looks like this:

function requestRuns(){
  console.log('request')
  return {
    type: 'getInitialRuns'
  }
}

export function getInitialRuns(){
  var url = 'http://localhost:32118/api/Runs';
  console.log(url);
  return dispatch => {
    dispatch(requestRuns())

    return fetch(url)
      .then(response => response.json().then(body => ({response, body})))
      .then(({response, body}) => {
        if(!response.ok){
          console.log('fail')
        }
        else{
          console.log('success')
        }
      })
  }

The component that calls the action looks like this:

class RunList extends Component{
  constructor(props) {
    super(props)
  }

  componentWillMount(){
    getInitialRuns()
  }

  render() {
    const {runs, isFetching, error} = this.props

    return (
      <ul className="run-list">
              {runs.map((run) => 
                  <Run key={run.Id} id={run.Id} date={run.DateString} day={run.Day} distance={run.Distance} duration={run.DurationString} avgpace={run.AvgPaceString} calories={run.Calories}/>
              )}
          </ul>
    )
  }
}

RunList.propTypes = {
  isFetching: PropTypes.bool.isRequired,
  runs: PropTypes.instanceOf(Immutable.List),
  error: PropTypes.object
}

function mapStateToProps(state) {
 return{
   runs: state.runs,
   isFetching: state.isFetching,
   error: state.error
 }
}

export default connect(mapStateToProps)(RunList)

My store is set up like this:

import { createStore, applyMiddleware } from 'redux';
import  {composeWithDevTools} from 'redux-devtools-extension';
import runs from './reducers/runs';
import thunk from 'redux-thunk';

export default createStore(runs,composeWithDevTools( applyMiddleware(thunk) ))

And these are my reducers

import Immutable from 'immutable'

let initialState = {
  runs: Immutable.List([]), 
  isFetching: false,
  error: undefined
};

export default (state = initialState, action) => {
  switch(action.type) {
    case 'addRun':
      return state.unshift(action.run)
    case 'deleteRun':
      return Object.assign({}, state, {
        runs: state.runs.filter((run) => run.Id !== action.id)
      })
    case 'getInitialRuns':
      console.log('initial')
      return Object.assign({}, state, {
        isFetching: true
      })
    case 'getInitialRunsSuccess':
    console.log('success')
      return Object.assign({}, state, {
        isFetching: false,
        runs: action.runs
      })
    case 'getInitialRunsFailure':
      return Object.assign({}, state, {
        isFetching: false,
        error: action.error
      })
    default:
      return state
  }
}
0

3 Answers 3

2

In order to dispatch an action on redux, you should provide a mapDispatchToProps function to connect. From redux docs:

(..) You can define a function called mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

I see you are using some advanced libraries like Immutablejs. I suggest you start with reading the awesome redux documentation as it will take you step by step. And until you're familiar with basic redux concepts, avoid any other library.

Here are some notes i hope are useful for you: (they are taken from redux docs)

Notes

  • An action object which is a payload of information that send data from your application to your store. It is of this form:

    var ADD_TODO = { type: ADD_TODO, text: 'Build my first Redux app' }

  • It's recommended to name you actions types in Upper case, like ADD_TODO.
  • dispatch accepts an action object (see the example above).
  • It is recommended to use action creators which are functions that return an action object. It makes them easily testable and portable
  • Action creators are usually named in lower case: addTodo().

I hope this helps a bit

Sign up to request clarification or add additional context in comments.

1 Comment

The mapDispatchToProps was what I needed. I will take the other best practices into consideration as well. Thank you!
0

You should be returning a new state if you want Redux to notice any state change.

Your reducer cases should look something like that:

return {
  ...previousState,
  newValues
}

'addRun'

return state.unshift(action.run)

should be

return {...state, runs: state.run.unshift(action.run) }

1 Comment

Thank you, I will keep that in mind. I don't see how this prevents the call to my API though? Am I missing something?
0

The function getInitialRuns returns a function, so calling it doesn't execute anything of the returned function. Btw I'm not sure executing it would be of any use for your app.

Reducers are synchronous, so if you need to do anything asynchronous you would need a middleware, like redux-thunk or redux-observable.

Flow should be:

State -> Views -> Actions -> Middleware -> Actions -> Reducers -> State and back to Views

Please look at the docs at http://redux.js.org/docs/advanced/AsyncActions.html

Also you can enjoy the excellent free courses on egghead:

https://egghead.io/courses/getting-started-with-redux

https://egghead.io/courses/building-react-applications-with-idiomatic-redux

On github you'll find a lot of material on the courses too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.