reactjs react-router-dom - كيفية الضغط على التاريخ في React Router v4؟




tutorial (13)

أقدم حلًا إضافيًا في حالة ما يستحقه لشخص آخر.

لدي ملف history.js حيث أجد ما يلي:

import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.push(...args))
export default history

بعد ذلك ، في جذر حيث أعرّف الموجه الخاص بي ، استخدم ما يلي:

import history from '../history'
import { Provider } from 'react-redux'
import { Router, Route, Switch } from 'react-router-dom'

export default class Root extends React.Component {
  render() {
    return (
     <Provider store={store}>
      <Router history={history}>
       <Switch>
        ...
       </Switch>
      </Router>
     </Provider>
    )
   }
  }

أخيرًا ، على أفعالي. js أستورد History واستفيد من pushLater

import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)

بهذه الطريقة ، يمكنني الضغط على إجراءات جديدة بعد استدعاءات واجهة برمجة التطبيقات.

آمل أن يساعد!

في الإصدار الحالي من React Router (الإصدار 3) يمكنني قبول استجابة الخادم واستخدام browserHistory.push للانتقال إلى صفحة الرد المناسبة. ومع ذلك ، هذا غير متوفر في الإصدار 4 ، ولست متأكدًا من الطريقة المناسبة للتعامل مع هذا الأمر.

في هذا المثال ، باستخدام Redux ، تقوم مكوّنات / app-product-form.js باستدعاء this.props.addProduct(props) عندما يرسل المستخدم النموذج. عندما يقوم الخادم بإرجاع نجاح ، يتم نقل المستخدم إلى صفحة سلة التسوق.

// actions/index.js
export function addProduct(props) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
      .then(response => {
        dispatch({ type: types.AUTH_USER });
        localStorage.setItem('token', response.data.token);
        browserHistory.push('/cart'); // no longer in React Router V4
      });
}

كيف يمكنني إجراء إعادة توجيه إلى صفحة سلة التسوق من وظيفة لـ React Router v4؟


إذا كنت تستخدم Redux ، فأنا أوصي باستخدام حزمة npm react-router-redux . يتيح لك إرسال إجراءات التنقل في متجر Redux.

يجب عليك إنشاء مخزن كما هو موضح في react-router-redux بهم.

أسهل حالة الاستخدام:

import { push } from 'react-router-redux'

this.props.dispatch(push('/second page'));

حالة الاستخدام الثانية مع حاوية / مكون:

حاوية:

import { connect } from 'react-redux';
import { push } from 'react-router-redux';

import Form from '../components/Form';

const mapDispatchToProps = dispatch => ({
  changeUrl: url => dispatch(push(url)),
});

export default connect(null, mapDispatchToProps)(Form);

مكون:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class Form extends Component {
  handleClick = () => {
    this.props.changeUrl('/secondPage');
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}/>
      </div>Readme file
    );
  }
}

سؤال مقرف ، أخذني الكثير من الوقت ، لكن في النهاية ، قمت بحل الأمر بهذه الطريقة:

لف الحاوية الخاصة بك مع withRouter وتمرير التاريخ إلى عملك في mapDispatchToProps وظيفة. في عمل استخدام history.push ('/ url') للتنقل.

عمل:

export function saveData(history, data) {
  fetch.post('/save', data)
     .then((response) => {
       ...
       history.push('/url');
     })
};

حاوية:

import { withRouter } from 'react-router-dom';
...
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    save: (data) => dispatch(saveData(ownProps.history, data))}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Container));

هذا صالح لـ React Router v4.x.


هنا هو الاختراق (هذا هو ملف الجذر الخاص بي ، مع القليل من redux مختلطة هناك - على الرغم من أنني لا أستخدم react-router-redux ):

const store = configureStore()
const customHistory = createBrowserHistory({
  basename: config.urlBasename || ''
})

ReactDOM.render(
  <Provider store={store}>
    <Router history={customHistory}>
      <Route component={({history}) => {
        window.appHistory = history
        return (
          <App />
        )
      }}/>
    </Router>
  </Provider>,
  document.getElementById('root')
)

يمكنني بعد ذلك استخدام window.appHistory.push() أي مكان أريده (على سبيل المثال ، في وظائف / reds / sagas store redux store) ، كنت آمل أن أتمكن فقط من استخدام window.customHistory.push() ولكن لسبب ما يبدو أن react-router لا يحدث على الرغم من تغيير عنوان url. ولكن بهذه الطريقة لدي يستخدم react-router سبيل المثال react-router بالضبط. لا أحب وضع الأشياء في النطاق العالمي ، وهذا أحد الأشياء القليلة التي سأفعلها بها. لكنه أفضل من أي بديل آخر رأيت المنظمة البحرية الدولية.


يمكنك استخدام هذا مثلما أفعل ذلك لتسجيل الدخول و manny أشياء مختلفة

class Login extends Component {
  constructor(props){
    super(props);
    this.login=this.login.bind(this)
  }


  login(){
this.props.history.push('/dashboard');
  }


render() {

    return (

   <div>
    <button onClick={this.login}>login</login>
    </div>

)

في هذه الحالة كنت تمرير الدعائم إلى thunk الخاص بك. لذلك يمكنك الاتصال ببساطة

props.history.push('/cart')

إذا لم تكن هذه هي الحالة ، فلا يزال بإمكانك تمرير السجل من المكون الخاص بك

export function addProduct(data, history) {
  return dispatch => {
    axios.post('/url', data).then((response) => {
      dispatch({ type: types.AUTH_USER })
      history.push('/cart')
    })
  }
}

React Router v4 يختلف اختلافًا جوهريًا عن v3 (والإصدارات الأقدم) ولا يمكنك القيام بـ browserHistory.push() كما browserHistory.push() .

يبدو أن هذه المناقشة مرتبطة إذا كنت تريد المزيد من المعلومات:

  • لن يعمل إنشاء browserHistory جديد browserHistory نظرًا لأن <BrowserRouter> يقوم بإنشاء مثيل محفوظات خاص به ، ويستمع إلى التغييرات على ذلك. لذلك ، سيغير موقع مختلف عنوان url ولكن لن يتم تحديث <BrowserRouter> .
  • لا يتم عرض browserHistory عن طريق جهاز التوجيه رد في v4 ، فقط في v2.

بدلا من ذلك لديك بعض الخيارات للقيام بذلك:

  • استخدم withRouter الأعلى من الترتيب

    بدلاً من ذلك ، يجب عليك استخدام مكون ترتيب مرتفع ، withRouter ذلك إلى المكون الذي سيدفع إلى المحفوظات. فمثلا:

    import React from "react";
    import { withRouter } from "react-router-dom";
    
    class MyComponent extends React.Component {
      ...
      myFunction() {
        this.props.history.push("/some/Path");
      }
      ...
    }
    export default withRouter(MyComponent);
    

    تحقق من الوثائق الرسمية لمزيد من المعلومات:

    يمكنك الوصول إلى خصائص كائن history وأقرب match <Route> عن طريق المكون الأعلى بالترتيب. مع rrader سوف يعيد عرض مكونه في كل مرة يتغير فيها المسار بنفس الدعائم مثل الدعائم render <Route> route <Route> : { match, location, history } .

  • استخدم context API

    قد يكون استخدام السياق واحدًا من أسهل الحلول ، ولكن كونه واجهة برمجة تطبيقات تجريبية ، فهو غير مستقر وغير مدعوم. استخدمها فقط عندما يفشل كل شيء آخر. إليك مثال على ذلك:

    import React from "react";
    import PropTypes from "prop-types";
    
    class MyComponent extends React.Component {
      static contextTypes = {
        router: PropTypes.object
      }
      constructor(props, context) {
         super(props, context);
      }
      ...
      myFunction() {
        this.context.router.history.push("/some/Path");
      }
      ...
    }
    

    إلقاء نظرة على الوثائق الرسمية في السياق:

    إذا كنت تريد أن يكون تطبيقك مستقرًا ، فلا تستخدم السياق. هو واجهة برمجة تطبيقات تجريبية ومن المحتمل أن يكسر الإصدارات المستقبلية من React.

    إذا كنت تصر على استخدام السياق على الرغم من هذه التحذيرات ، فحاول عزل استخدامك للسياق إلى منطقة صغيرة وتجنب استخدام واجهة برمجة التطبيقات للسياق مباشرة عندما يكون ذلك ممكنًا بحيث يكون من الأسهل الترقية عند تغيير واجهة برمجة التطبيقات.


يمكنك استخدام طرق history خارج المكونات الخاصة بك. حاول بالطريقة التالية.

أولاً ، أنشئ كائن history استخدم حزمة المحفوظات :

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

ثم لفه في <Router> ( يرجى ملاحظة أنه يجب عليك استخدام import { Router } بدلاً من import { BrowserRouter as Router } ):

// src/index.jsx

// ...
import { Router, Route, Link } from 'react-router-dom';
import history from './history';

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <div>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/login">Login</Link></li>
        </ul>
        <Route exact path="/" component={HomePage} />
        <Route path="/login" component={LoginPage} />
      </div>
    </Router>
  </Provider>,
  document.getElementById('root'),
);

غيّر موقعك الحالي من أي مكان ، على سبيل المثال:

// src/actions/userActionCreators.js

// ...
import history from '../history';

export function login(credentials) {
  return function (dispatch) {
    return loginRemotely(credentials)
      .then((response) => {
        // ...
        history.push('/');
      });
  };
}

UPD : يمكنك أيضًا مشاهدة مثال مختلف بعض الشيء في الأسئلة الشائعة حول "رد الفعل الموجه" .


لن يعمل هذا. this.context.history.push .

تمكنت من الحصول على عمل مثل هذا:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

    if (this.props.auth.success) {
        this.context.router.history.push("/some/Path")
    }

}

كنت قادرا على تحقيق ذلك باستخدام bind() . أردت النقر على زر في index.jsx ، ونشر بعض البيانات على الخادم ، وتقييم الاستجابة ، وإعادة التوجيه إلى success.jsx . هنا كيف عملت ذلك ...

index.jsx :

import React, { Component } from "react"
import { postData } from "../../scripts/request"

class Main extends Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
        this.postData = postData.bind(this)
    }

    handleClick() {
        const data = {
            "first_name": "Test",
            "last_name": "Guy",
            "email": "[email protected]"
        }

        this.postData("person", data)
    }

    render() {
        return (
            <div className="Main">
                <button onClick={this.handleClick}>Test Post</button>
            </div>
        )
    }
}

export default Main

request.js :

import { post } from "./fetch"

export const postData = function(url, data) {
    // post is a fetch() in another script...
    post(url, data)
        .then((result) => {
            if (result.status === "ok") {
                this.props.history.push("/success")
            }
        })
}

success.jsx :

import React from "react"

const Success = () => {
    return (
        <div className="Success">
            Hey cool, got it.
        </div>
    )
}

export default Success

لذلك من خلال ربط this بـ postData في index.jsx ، فقد تمكنت من الوصول إلى هذا this.props.history في request.js ... ثم يمكنني إعادة استخدام هذه الوظيفة في مكونات مختلفة ، عليك فقط التأكد من تذكر تضمين this.postData = postData.bind(this) في constructor() .


استخدم رد الاتصال. عملت معي!

export function addProduct(props, callback) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
    .then(response => {
    dispatch({ type: types.AUTH_USER });
    localStorage.setItem('token', response.data.token);
    callback();
  });
}

في المكون ، عليك فقط إضافة الاستدعاء

this.props.addProduct(props, () => this.props.history.push('/cart'))

وفقًا لتوثيق React Router v4 - جلسة عمل Redux Deep Integration

هناك حاجة إلى تكامل عميق من أجل:

"كن قادراً على التنقل عن طريق إرسال الإجراءات"

ومع ذلك ، فإنهم يوصون بهذا النهج كبديل عن "التكامل العميق":

"بدلاً من إرسال إجراءات للتنقل ، يمكنك تمرير كائن السجل المقدم لمكونات الطريق إلى إجراءاتك والتنقل معه هناك."

بحيث يمكنك التفاف المكون الخاص بك مع مكون ترتيب مرتفع مع:

export default withRouter(connect(null, { actionCreatorName })(ReactComponent));

والتي ستمر على واجهة برمجة التطبيقات للدعايات. لذلك يمكنك الاتصال بمُبدع الإجراءات الذي يجتاز التاريخ كممثل. على سبيل المثال ، داخل ReactComponent:

onClick={() => {
  this.props.actionCreatorName(
    this.props.history,
    otherParams
  );
}}

ثم ، داخل إجراءاتك / index.js:

export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.push("/path");
  };
}

إن هدف أبراموف - ومن وجهة نظر الجميع - هو ببساطة لتغليف التعقيد (والمكالمات غير المتزامنة) في المكان المناسب .

أين هو أفضل مكان للقيام بذلك في البيانات القياسية من Redux؟ كيف حول:

  • علب التروس ؟ لا يمكن. يجب أن تكون وظائف نقية بدون أي آثار جانبية. تحديث المتجر هو عمل خطير ومعقد. لا تلوثها.
  • مكونات العرض البكم؟ بالتأكيد لا. لديهم قلق واحد: العرض التقديمي وتفاعل المستخدم ، وينبغي أن يكون بسيطا قدر الإمكان.
  • مكونات الحاوية؟ ممكن ، ولكن دون المستوى الأمثل. من المنطقي أن تكون الحاوية مكانًا نسطر فيه بعض التعقيدات المرتبطة بالتفاعل وتتفاعل مع المتجر ، ولكن:
    • تحتاج الحاويات إلى أن تكون أكثر تعقيدًا من المكونات الغبية ، ولكنها تظل مسئولية واحدة: توفير الارتباطات بين العرض والدولة / المتجر. منطقك غير المتزامن هو مصدر قلق منفصل تمامًا عن ذلك.
    • بوضعه في حاوية ، سيتم قفل منطق متزامن في سياق واحد ، لطريقة عرض واحدة / مسار واحد. فكرة سيئة. من الناحية المثالية انها كلها قابلة لإعادة الاستخدام ، وفكها تماما.
  • S ome وحدة خدمة أخرى؟ فكرة سيئة: ستحتاج إلى حقن الوصول إلى المتجر ، وهو كابوس الصيانة / القابلية للاختبار. من الأفضل الذهاب مع حبوب Redux والوصول إلى المتجر فقط باستخدام واجهات برمجة التطبيقات / النماذج المقدمة.
  • الإجراءات و Interwar التي تفسر لهم؟ لما لا؟! بالنسبة للمبتدئين ، هذا هو الخيار الرئيسي الوحيد المتبقي لدينا. :-) أكثر منطقية ، هو فصل نظام التنفيذ المنطق التنفيذ الذي يمكنك استخدامه من أي مكان. يتم الوصول إلى المتجر ويمكنه إرسال المزيد من الإجراءات. لديه مسؤولية واحدة وهي تنظيم تدفق السيطرة والبيانات حول التطبيق ، ومعظمها غير ملائم يلائم ذلك.
    • ماذا عن المبدعين العمل؟ لماذا لا تقوم بالتزامن فقط ، بدلاً من القيام بالأفعال نفسها ، وفي البرامج الوسيطة؟
      • أولاً والأكثر أهمية ، لا يمتلك منشئو المحتوى حق الوصول إلى المتجر ، كما يفعل الوسيطة. وهذا يعني أنه لا يمكنك إرسال إجراءات طارئة جديدة ، ولا يمكن قراءتها من المتجر لإنشاء برنامج التزامن الخاص بك ، إلخ.
      • لذا ، حافظ على التعقيد في مكان معقد من الضرورات ، واحتفظ بكل شيء آخر بسيطًا. يمكن للمبدعين بعد ذلك أن يكونوا وظائف بسيطة نقية وسهلة الاختبار.






reactjs redux react-router react-router-v4