【问题标题】:Changing the url in React/Redux在 React/Redux 中更改 url
【发布时间】:2018-05-17 19:03:33
【问题描述】:

我正在尝试使用 React 和 Redux (DVA) 构建应用程序。它使用 Ant.Design 作为主要框架。我试图在用户单击按钮时更改 URL,并且显然将该 url 更改“绑定”到一个操作,这样如果用户直接访问该 URL,他就会得到他想要的。

目前这就是我在组件中的一个函数中所拥有的。

const { dispatch, match } = this.props;
dispatch(routerRedux.push('/f/' + record.id));

这是我唯一能够制作的东西。它正确地更改了 url,但没有将 url 与特定行为绑定,使其完全无用。

如何链接 URL 与操作?

【问题讨论】:

    标签: javascript reactjs redux react-router antd


    【解决方案1】:

    如果您希望基于 URL 触发操作,您需要使用 react-router 来路由一个组件,然后执行所需的操作。在这种情况下,访问不同的 URL 也是一个好主意,从浏览器的历史记录中删除操作 URL。

    典型的路由器定义可能如下所示(取自react-router-redux's docs):

    ReactDOM.render(
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <div>
            <Route exact path="/" component={Home}/>
            <Route path="/about" component={About}/>
            <Route path="/success" component={Success}/>
          </div>
        </ConnectedRouter>
      </Provider>,
      document.getElementById('root')
    )
    

    所以你想添加一个路径/f/&lt;record-id&gt;。你可以通过添加这样的一行来做到这一点:

    <Route path="/f/:recordId" component={MyActionComponent}/>
    

    现在您需要定义一个组件MyActionComponent,它将执行您的操作。

    import { connect } from 'react-redux';
    import { replace } from 'react-router-redux';
    
    const mapDispatchToProps = (dispatch: Dispatch) => ({
      visitNextLocation: () => dispatch(replace('/success')),
      myAction: (recordId) => dispatch(myAction(recordId)),
    });
    
    const withDispatch = connect(null, mapDispatchToProps);
    
    class MyActionComponent extends Component {
      props: {
        match: {
          params: {
            recordId: string,
          }
        },
        redirectToLogin: () => void,
        myAction: string => void,
      };
    
      componentWillMount() {
        const recordId = this.props.match.params.recordId;
        if (recordId) {
          this.props.myAction(token);
          this.props.visitNextLocation();
        }
      }
    
      render() {
        return null;
      }
    }
    

    注意使用replace 而不是push。这意味着,当用户访问此 URL 时,他们的操作将被执行,他们最终将访问 /success。但是,如果他们单击“后退”按钮,他们将不会重新访问此 URL 并再次运行该操作。

    【讨论】:

    • 感谢您的帮助,但不幸的是,我的应用程序的设计方式是将每个视图分为两部分。 left 面板始终保持不变并包含一堆链接,每个链接都会改变用户在 right 面板中看到的内容。所以基本上,我有一个名为“用户”的组件,其中包含 2 个组件:LeftPanel 和 RightPanel。因此我有&lt;Route path="/users" component={users}/&gt;,当我尝试 localhost:8000/users 时它确实工作正常,但是在用户组件内部,组件 LeftPanel 也应该修改 URL
    • 你能分享一个例子,也许使用codepen?你所说的关于修改 URL 的组件让我感到困惑。您是说某个操作导致push 调度还是该组件正在以其他方式修改URL?还是您在谈论路由?
    • 你能看看我对这个问题的回答吗?
    【解决方案2】:

    出于隐私原因,我无法将代码放在 Codepen 上。但这里有一段摘录:

    路由器.js

    ...
    },
    '/users': {
      component: dynamicWrapper(app, ['rule'], () => import('../routes/users')),
    },
    '/f/:userID': {
      component: dynamicWrapper(app, ['rule'], () => import('../routes/users')),
    },
    ...
    

    users.js(包含 LeftPanel 和 RightPanel 的主要组件)

    import React, { PureComponent } from 'react';
    import { connect } from 'dva';
    import { Row, Col, Card, List, Divider, Badge, Select, Radio, Input, Popover, Button, Table, Spin } from 'antd';
    import RightPanel from './RightPanel';
    import LeftPanel from './LeftPanel';
    import { routerRedux, Route, Switch } from 'dva/router';
    import 'font-awesome/css/font-awesome.min.css';
    import FadeIn from 'react-fade-in';
    
    @connect(({ rule, loading }) => ({rule, loading: loading.models.rule }))
    export default class Users extends React.Component {
          constructor(props) {
            super(props)
            this.state = {
              selected_user: [],
              defaultView: true,
              isLoadingNow: false,
              selectedRowKeys: [],
              makeEmpty: false,
              searchRes: []
          }
        }
    
            selectRow = (record) => {       
                const { dispatch, match } = this.props;
                dispatch(routerRedux.replace({ pathname: '/f/' + record.id }));
    
                this.setState({isLoadingNow: true, selectedRowKeys: record.key})
                setTimeout(() => {
                  this.setState({
                        isLoadingNow: false,
                        defaultView: false,
                        selected_user: record
                    })
                }, 75)
            }
    
            componentDidMount() { 
                const { dispatch, match } = this.props;
                dispatch({
                    type: 'rule/fetch'
                });
                if (match.params.userID == undefined) {
                    // do nothing
                } else if (match.params.userID) {
                    var result = this.props.rule.data.list.filter(function( obj ) {
                      return obj.id == match.params.userID;
                    });
                    this.selectRow.bind(this, result[0])
                }
            }
    
            render() {
    
                const { selectedRowKeys } = this.state;
                const rowSelection = {
                    selectedRowKeys,
                    type:"radio"
                };
    
                const { rule: { data }, loading } = this.props;
    
                return (<div>
    
                                <LeftPanel
                                    rowSelection={rowSelection}
                                    dataSource={this.state.makeEmpty ? this.state.searchRes : this.props.rule.data.list} 
                                    selectRow={this.selectRow} 
                                    loadingStatus={loading}
                                />
                              <RightPanel 
                                    selected_user={this.state.selected_user}
                                    is_default={this.state.defaultView}
                                    loading={this.state.isLoadingNow}
                              />
                            </div>);
    
    
    }
    }
    

    leftPanel.js(负责显示链接列表,用户将点击其中的一个,这将做两件事:
    - 相应地更改网址
    - 在 RightPanel.js 上显示特定数据)

    import React from 'react';
    import { Table, Card } from 'antd';
    
    import styles from './index.less';
    // import 'font-awesome/css/font-awesome.min.css';
    
    import { Row, Col, List, Divider, Badge, Select, Radio, Input, Popover, Button } from 'antd';
    
    var moment = require('moment');
    
    class LeftPanel extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                selected_row_index: undefined
            }
        }   
        handleChangeStyleOnSelectRow(index) {
            this.setState({
                selected_row_index: index
            }, console.log(this.state.selected_row_index))
        }
    
        prettifyForTable(raw_data) {
          var prettyRows = [];
          raw_data.map((item,index) =>
            prettyRows.push(
              <div style={{"width": "100%"}}> 
                <Row style={{  "align-items": "center"}} type="flex" justify="space-between">
                    <Col span={10}>
                        <div style={{"font-size": "15px", "text-align": "center"}}>
                            {item.user_name} <i style={{"color": "rgba(0, 0, 0, 0.25)", "margin": "0 10px", "transform": "rotate(45deg)"}} className="fa fa-plane"> </i> {item.user_age}
                            <div style={{"font-size": "12px", "color": "grey"}}> {moment(item.user_color).format('HH:MM')} · {moment(item.user_order).format('HH:MM')}  </div>
                        </div>
                    </Col>
                    <Col span={3}>
                        <div style={{"text-align": "right", "text-align": "center"}}>
                          {item.user_family}
                        </div>
                    </Col>
                    <Col span={6}>
                        <div style={{"text-align": "right", "text-align": "center"}}>
                          {moment(item.user_height).format('MMMM D')}
                        </div>
                    </Col>
                    <Col span={3}>
                        <div style={{"text-align": "center"}}>
                            {(item.status == "in_progress") ? <div> <Badge style={{"padding-right": "25px"}} status="processing"/></div> : <div style={{"text-align": "center"}}> <Badge style={{"padding-right": "25px"}} status="default"/></div>}
                        </div>
                    </Col>
                </Row>
              </div>
            )
          );
          return prettyRows;
        }
    
    
      render() {
        const stylesSelectedRow = { "background": "rgba(155,155,155,0.05)", "box-shadow": "0 0 5px 0 #4A90E2", "transform": "scale(1.01)"};
        const { dataSource } = this.props;
            return(
             <div>
                {dataSource &&
              <Card bordered={false} loading={this.props.loadingStatus} className={styles.userRows} bodyStyle={{"padding": "0 15px"}}>
                    <List
                      size="small"
                      bordered={false}
                      dataSource={this.prettifyForTable(dataSource)}
                      renderItem={(item, index) => (<List.Item onClick={() => {this.state.selected_row_index == index ? null : this.props.selectRow(this.props.dataSource[index]); this.handleChangeStyleOnSelectRow(index)}} style={this.state.selected_row_index == index ? stylesSelectedRow : null} className={styles.userRows}>{item}</List.Item>)}
                    />
              </Card>
                }
            </div>
        )
      }
    }
    
    export default LeftPanel;
    

    最后是 RightPanel.js,它负责侦听 URL 或单击 LeftPanel,并相应地显示数据。

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import infopng from '../../../assets/info.svg';
    
    import TabInformations from './TabInformations.js';
    import TabFamily from './TabFamily.js';
    import TabProblems from './TabProblems.js';
    import { Tabs, Button, Spin, Icon, Table, Pagination, Card, Col, Row, Spinner, Badge } from 'antd';
    const TabPane = Tabs.TabPane;
    import 'font-awesome/css/font-awesome.min.css';
    import WindowSizeListener from 'react-window-size-listener'
    import FadeIn from 'react-fade-in';
    
    export default class RightPanel extends Component {
    
      render() {
    
        if (this.props.loading) {
            return(
                <div> 
              <Spin 
                indicator={<Icon type="loading" style={{ fontSize: 24 }} spin />}
                        src="http://web.gndu.ac.in/DepartmentProfile/images/spinner.gif"
                    />
                </div>
            );
        } else if (this.props.is_default) {
          return(
              <div style={{"margin-top": "64px", "margin-right": "10px", "text-align": "center", "height": "90vh", "display": "flex", "align-items": "center", "justify-content": "center"}}> 
                <div>
                    <img src={infopng} style={{"height": "155px"}} />
                    <p style={{"color": "#8e8e8e"}}> select a user on the <br/> left-hand side... </p>
                   </div>
              </div>
            );
        } else {
        return (
    
          <FadeIn>
    
          <Card bodyStyle={{"padding": "0"}} style={{"background-color": "white", "height":"90vh", "padding": "20px", "box-shadow": "rgba(0, 21, 41, 0.1) 0px 0px 6px", "opacity": "1", "transition": "background 0.6s", "border-radius": "2px", "margin": "10px 10px 0 0px", "margin-top": "64px"}}> 
                <Tabs defaultActiveKey="1" style={{"text-align": "center", "padding": "0 15px"}}>
            <TabPane tab="General" key="1">
              <TabInformations
                        selected_user={this.props.selected_user}
                    />
                </TabPane>
                <TabPane tab="Servicing" key="2">
              <TabFamily 
                selected_user={this.props.selected_user} 
              />
              </TabPane>
              <TabPane tab={<div>Defect(s)<Badge showZero count={0} style={{ backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset', "margin-left": "10px" }} /></div>} key="3">
              <TabProblems />
            </TabPane>
            </Tabs>
        </Card>
        </FadeIn>
        );
      }
    }
    }
    

    这段代码很好地完成了初始工作:当用户点击 leftPanel.js 上的链接时,leftPanel.js 会调用 users.js 中的 selectRow 方法,然后选择一行并将其显示在 RightPanel 上。 js.

    我的问题:如何添加它会在用户点击时更改 URL 的事实?显然,如果用户在 Chrome 中点击“返回”,RightPanel.js 中的数据必须相应更改.. ?

    【讨论】:

    • 这和我以为你问的完全不同!在我看来,您正在尝试访问不同的 URL,但不是实际访问它,而是通过users.js 中的状态管理您正在查看的内容。我认为如果您将selected_user 开到this.props 并在selectRow 中进行dispatch(router.push(...)) 会更容易。 .push 会在您的历史记录中添加一个 URL,这样当您单击返回时,它会记住您之前的位置。
    • 我之前没有使用过dva,但您可能希望将LeftPanel 移动为其他路由的兄弟,因为它是跨路由共享的。你可以在这里看到一个导航的例子:tylermcginnis.com/react-router-nested-routes(我认为它只适用于 react-router v4)
    猜你喜欢
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    相关资源
    最近更新 更多