2

I am using react-router and react-router-redux to handle navigation on my page. I need change my url programmatically inside component. I was trying to use this method: history.push to achieve this but this method is only change the url and component associated with this url is not updated. This app is simple list with pagination so when i switch to the next page url is changing for example /posts/1 to /posts/2 but view is not updated. I think this should work like this:

  1. User click pagination item and click handler is called passing page number as argument
  2. Inside click handler i call history.push(/posts/[page]). I could use Link component but i want to be able to do something when user click pagination item
  3. I expect that my ObjectList component will be mounted again and componentDidMount will be called

This is probably not the best aproach so i will be greatfull for tips links are hardcoded especially first argument My source code:

client.js

import React from "react";
import ReactDOM from "react-dom";
import {Router, Route, IndexRoute, browserHistory} from "react-router";
import Results from "./views/Results";
import Home from "./views/Home";
import App from './components/App'
import { Provider } from 'react-redux';
import store, { history } from './store';


const app = document.getElementById('app');

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
        <Route path="/" component={App}>
        <IndexRoute component={Home} />
        <Route path="/:category/:cityId/:pageNum" component={Results}></Route>
    </Route>
    </Router>
  </Provider>,
  app
);

store.js

import { createStore, compose, applyMiddleware } from 'redux'
import { syncHistoryWithStore } from 'react-router-redux'
import thunkMiddleware from 'redux-thunk'
import { browserHistory } from 'react-router'
import rootReducer from './reducers/index'
import createLogger from 'redux-logger'
import categories from './data/categories'

const loggerMiddleware = createLogger()

const defaultState = {
    categories,
    resultsList: {
      objects: [],
      counters: [],
      isFetching: false
    }
};

const store = createStore(
  rootReducer,
  defaultState,
  compose (
    applyMiddleware(
      thunkMiddleware,
      loggerMiddleware
    ),
    window.devToolsExtension ? window.devToolsExtension() : f => f
  )
);
export const history = syncHistoryWithStore(browserHistory, store)
export default store

ObjectList.js

import React from "react";
import ObjectItem from "../components/ObjectItem"
import Loader from "../components/Loader"
import fetchObjects from "../actions/actionCreators";
import switchUrl from "../actions/actionCreators";
import PaginationPanel from "../components/PaginationPanel"
import classNames from 'classnames'
import { push } from 'react-router-redux';
import { browserHistory } from 'react-router'
import store, { history } from '../store';


export default class ObjectList extends React.Component {
  static defaultProps = {
      objectsPerPage: 20,
      objectContainerClassName: 'object_list_items'
  }

  constructor(props) {
      super(props);
  }

  componentDidMount() {
    this.props.fetchObjects(this.props.params.pageNum);
  }

  paginateHandler(page) {
      this.props.history.push('/hotele/1/'+page)
  }

  render() {
    const { resultsList } = this.props

    if(resultsList.items.length > 0) {
      const ObjectComponents = resultsList.items.map((item) => {
          return <ObjectItem key={item.post_id} {...item}/>;
      });

      const paginationComponent =
        <PaginationPanel
            {...this.props}
            pageNum={Math.ceil(resultsList.counters.allPosts/this.props.objectsPerPage)}
            pageClickedHandler={this.paginateHandler.bind(this)}
            currentPage={parseInt(this.props.params.pageNum)}
        />

      return (
        <div className="object-lists">
            <div className={this.props.objectContainerClassName}>
                <div>{ObjectComponents}</div>
            </div>
            {paginationComponent}
        </div>
      )
    }
    else if(!resultsList.isFetching || resultsList.items.length === 0) {
      return <Loader />;
    }
  }
}

Home.js

import React from "react"
import { Link } from "react-router"


const Home = React.createClass({
  render() {
    return (
      <div>
          Strona główna <br />
      <Link to={`/hotele/1/1`}>Lista wyszukiwania</Link>
      </div>
    )
  }
})

export default Home

Results.js

import React from "react";
import ObjectList from "../components/ObjectList"
import CategoryTabs from "../components/CategoryTabs"
import fetchObjects from "../actions/actionCreators"


export default class Results extends React.Component{
  constructor(props) {
    super(props);
  }

  render() {
      return (
          <div>
              <CategoryTabs { ...this.props } />
              <ObjectList { ...this.props } />
          </div>
      );
  }
}

reducers/index.js

import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'

import objects from './objects'
import categories from './categories'

const rootReducer = combineReducers({objects, categories, routing: routerReducer})

export default rootReducer

reducers/objects.js

function objects(state = {
  isFetching: false,
  items: [],
  counters: []
}, action) {
  switch (action.type) {
    case 'RECEIVE_OBJECTS':
      return Object.assign({}, state, {
        isFetching: false,
        items: action.objects.posts,
        counters: action.objects.counters
      })
    default:
      return state;
  }
}

export default objects

app.js

import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actionCreators from '../actions/actionCreators';
import Main from '../components/Main';


function mapStateToProps(state) {
  return {
    resultsList: state.objects,
    categories: state.categories
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch);
}

const App = connect(mapStateToProps, mapDispatchToProps)(Main);

export default App;

actionCreators.js

import fetch from 'isomorphic-fetch'
import { push } from 'react-router-redux';


function receiveObjects(objects, json) {
  return {
    type: 'RECEIVE_OBJECTS',
    objects
  }
}

function requestObject(pageNum) {
  return {
    type: 'REQUEST_OBJECTS',
    pageNum
  }
}

export function fetchObjects(pageNum) {
  return dispatch => {
      dispatch(requestObject(pageNum));

      let url = 'http://localhost:8080/posts?city=986283&type=hotel&page='+pageNum;

      return fetch(url)
        .then(response => response.json())
        .then(json => dispatch(receiveObjects(json)));
  }
}

1 Answer 1

2

ObjectList component will not be mounted again because you are not changing components tree. It is still

<Home>
    <Results>
        <ObjectList />
    </Results>
</Home>

It will be remounted only if you go to a different route and mount different root component so the whole tree would change. But You're just passing different props. You need to use

componentWillReceiveProps(nextProps) {
  this.props.fetchObjects(nextProps.params.pageNum);
}
Sign up to request clarification or add additional context in comments.

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.