如何使用 reactjs 和 redux 处理导航

Posted

技术标签:

【中文标题】如何使用 reactjs 和 redux 处理导航【英文标题】:How to handle navigation using reactjs and redux 【发布时间】:2016-10-27 21:16:51 【问题描述】:

我正在使用 react-router 和 react-router-redux 来处理我页面上的导航。我需要在组件内以编程方式更改我的 url。我试图使用这种方法:history.push 来实现这一点,但这种方法只是更改 url 并且与此 url 关联的组件不会更新。这个应用程序是带有分页的简单列表,所以当我切换到下一页时,url 会发生变化,例如 /posts/1 到 /posts/2 但视图没有更新。我认为这应该像这样工作:

    用户单击分页项和单击处理程序称为传递 页码作为参数 在点击处理程序中,我调用了 history.push(/posts/[page])。我可以 使用链接组件,但我希望能够在用户做某事 点击分页项 我希望我的 ObjectList 组件将再次挂载,并且 componentDidMount 将被调用

这可能不是最好的方法,所以我会很高兴获得提示 链接是硬编码的,尤其是第一个参数 我的源代码:

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】:

ObjectList 组件将不会再次挂载,因为您没有更改组件树。还是

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

只有当您转到不同的路线并安装不同的根组件时才会重新安装它,这样整个树都会改变。但你只是传递不同的道具。你需要使用

componentWillReceiveProps(nextProps) 
  this.props.fetchObjects(nextProps.params.pageNum);

【讨论】:

以上是关于如何使用 reactjs 和 redux 处理导航的主要内容,如果未能解决你的问题,请参考以下文章

react+redux 中如何通过请求动作处理中间件并触发成功动作?

如何正确地从 ReactJS + Redux 应用程序进行 REST 调用?

ReactJS:如何使用 Formik 处理图像/文件上传?

ReactJS:处理空间导航(键盘和游戏手柄)[关闭]

如何使用 MongoDB + NodeJS Express 向 ReactJS React Router 和 Redux 添加登录身份验证和会话?

当我使用 ReactJS 从数据表中删除行而不使用 Redux 或 Context 时,如何刷新数据表?