นำทางแบบเป็นโปรแกรมโดยใช้ react router V4


336

ฉันเพิ่งเปลี่ยนreact-routerจาก v3 เป็น v4 แต่ผมไม่แน่ใจว่าจะเลื่อนโปรแกรมในฟังก์ชั่นสมาชิกของ
Componentนั่นคือในhandleClick()ฟังก์ชั่นที่ฉันต้องการนำทาง/path/some/whereหลังจากประมวลผลข้อมูล ฉันเคยทำโดย:

import { browserHistory } from 'react-router'
browserHistory.push('/path/some/where')

แต่ฉันไม่พบอินเทอร์เฟซดังกล่าวใน v4
ฉันจะนำทางโดยใช้ v4 ได้อย่างไร


3
คุณสามารถเข้าถึงวัตถุประวัติใน v4 ผ่านอุปกรณ์ประกอบฉาก: this.props.history.push ('/')
colemerrick

6
เกิดอะไรขึ้นถ้าคุณต้องการที่จะเข้าถึงได้จากสถานที่ที่แตกต่างกันกว่าComponent? ตัวอย่างเช่นภายในการดำเนินการ redux
Maximus S

73
บางครั้งฉันสงสัยว่าทำไมมันซับซ้อนมากเพียงแค่ย้ายจากลิงค์หนึ่งไปยังอีก =))
Thai Tran

12
ใครจะคิดว่าในวันนี้และอายุการเปลี่ยนเส้นทางจะไม่ซับซ้อนนัก
JohnAllen

ฉันพบโพสต์นี้มีประโยชน์: medium.com/@anneeb/redirecting-in-react-4de5e517354a
BioData41

คำตอบ:


414

หากคุณกำหนดเป้าหมายสภาพแวดล้อมที่เบราว์เซอร์ที่คุณต้องใช้แพคเกจแทนreact-router-dom react-routerพวกเขาปฏิบัติตามวิธีเดียวกันกับที่ React ทำเพื่อแยกคอร์ ( react) และโค้ดเฉพาะแพล็ตฟอร์ม ( react-dom, react-native) ที่มีความแตกต่างเล็กน้อยที่คุณไม่จำเป็นต้องติดตั้งสองแพ็คเกจแยกกันดังนั้นแพ็คเกจสภาพแวดล้อมจึงมีทุกสิ่ง คุณต้องการ. คุณสามารถเพิ่มลงในโครงการของคุณเป็น:

yarn add react-router-dom

หรือ

npm i react-router-dom

สิ่งแรกที่คุณต้องทำคือจัดให้มี<BrowserRouter>ส่วนประกอบหลักที่เป็นพาเรนต์สูงสุดในแอปพลิเคชันของคุณ <BrowserRouter>ใช้ HTML5 historyAPI และจัดการให้กับคุณดังนั้นคุณไม่ต้องกังวลเกี่ยวกับการสร้างอินสแตนซ์ด้วยตนเองและส่งผ่านไปยัง<BrowserRouter>คอมโพเนนต์ในรูปแบบ prop (ตามที่คุณจำเป็นต้องทำในเวอร์ชันก่อนหน้า)

ใน V4 สำหรับการนำทางโดยทางโปรแกรมคุณจำเป็นต้องเข้าถึงhistoryออบเจกต์ซึ่งมีให้ผ่าน React contextตราบใดที่คุณมีส่วนประกอบของ<BrowserRouter> ผู้ให้บริการเป็นพาเรนต์สูงสุดในแอปพลิเคชันของคุณ ไลบรารีจะเปิดเผยตามบริบทที่routerวัตถุนั้นมีอยู่historyเป็นคุณสมบัติ historyอินเตอร์เฟซที่นำเสนอวิธีการนำทางต่างๆเช่นpush, replaceและgoBackอื่น ๆ ในกลุ่ม ท่านสามารถตรวจสอบรายชื่อทั้งหมดของคุณสมบัติและวิธีการที่นี่

หมายเหตุสำคัญสำหรับผู้ใช้ Redux / Mobx

หากคุณกำลังใช้ redux หรือ mobx เป็นไลบรารีการจัดการสถานะของคุณในแอปพลิเคชันของคุณคุณอาจพบปัญหาเกี่ยวกับส่วนประกอบที่ควรทราบตำแหน่ง แต่จะไม่ถูกแสดงผลซ้ำหลังจากเรียกการอัพเดต URL

เกิดขึ้นเพราะreact-routerส่งผ่านlocationไปยังส่วนประกอบโดยใช้โมเดลบริบท

ทั้งการเชื่อมต่อและผู้สังเกตการณ์สร้างส่วนประกอบที่มีวิธีการ ComponentUpdate ทำการเปรียบเทียบตื้น ๆ ของอุปกรณ์ประกอบฉากในปัจจุบันและอุปกรณ์ประกอบฉากต่อไปของพวกเขา ส่วนประกอบเหล่านั้นจะแสดงผลซ้ำเมื่อเปลี่ยนเสาอย่างน้อยหนึ่งเสา ซึ่งหมายความว่าเพื่อให้แน่ใจว่าพวกเขาอัปเดตเมื่อมีการเปลี่ยนแปลงสถานที่พวกเขาจะต้องได้รับเสาที่เปลี่ยนแปลงเมื่อมีการเปลี่ยนแปลงสถานที่

2 แนวทางสำหรับการแก้ปัญหานี้คือ:

  • ห่อของคุณเชื่อมต่อ<Route />องค์ประกอบในไม่มีทางไป locationวัตถุปัจจุบันเป็นหนึ่งในอุปกรณ์ประกอบฉากที่<Route>ผ่านไปยังส่วนประกอบที่มันแสดงผล
  • ห่อส่วนประกอบที่เชื่อมต่อของคุณด้วยส่วนประกอบลำดับwithRouterสูงกว่าซึ่งอันที่จริงมีผลเหมือนกันและlocationใช้เป็นเสาแทน

นอกจากนั้นยังมีสี่วิธีในการนำทางโดยเรียงลำดับตามคำแนะนำ:

1.- การใช้<Route>ชิ้นส่วน

มันส่งเสริมสไตล์ที่เปิดเผย ก่อนหน้า v4 <Route />ส่วนประกอบถูกวางไว้ที่ด้านบนสุดของลำดับชั้นส่วนประกอบของคุณโดยไม่ต้องคิดถึงโครงสร้างเส้นทางของคุณก่อน อย่างไรก็ตามตอนนี้คุณสามารถมี<Route>ส่วนประกอบได้ทุกที่ในทรีของคุณเพื่อให้คุณสามารถควบคุมการแสดงผลได้อย่างมีเงื่อนไขขึ้นอยู่กับ URL Routeอัดฉีดmatch, locationและhistoryเป็นอุปกรณ์ประกอบฉากเข้าไปในคอมโพเนนต์ของคุณ วิธีนำทาง (เช่นpush, replace, goBack... ) มีอยู่เป็นคุณสมบัติของhistoryวัตถุ

มี 3 วิธีที่จะทำให้สิ่งที่มีอยู่Routeโดยใช้อย่างใดอย่างหนึ่งcomponent, renderหรือchildrenอุปกรณ์ประกอบฉาก Routeแต่ไม่ได้ใช้มากกว่าหนึ่งในเดียวกัน ตัวเลือกขึ้นอยู่กับกรณีการใช้งาน แต่โดยทั่วไปแล้วสองตัวเลือกแรกจะแสดงองค์ประกอบของคุณหากpathตรงกับตำแหน่ง URL ในขณะchildrenที่องค์ประกอบจะถูกแสดงว่าเส้นทางตรงกับตำแหน่งหรือไม่ (มีประโยชน์สำหรับการปรับ UI ตาม URL การจับคู่)

หากคุณต้องการที่จะกำหนดผลลัพธ์การแสดงผลของคุณองค์ประกอบคุณจะต้องห่อคอมโพเนนต์ของคุณในการทำงานและใช้renderตัวเลือกเพื่อที่จะผ่านไปยังส่วนของอุปกรณ์ประกอบฉากอื่น ๆ ที่คุณต้องการนอกเหนือจากmatch, และlocation historyตัวอย่างที่แสดงให้เห็น:

import { BrowserRouter as Router } from 'react-router-dom'

const ButtonToNavigate = ({ title, history }) => (
  <button
    type="button"
    onClick={() => history.push('/my-new-location')}
  >
    {title}
  </button>
);

const SomeComponent = () => (
  <Route path="/" render={(props) => <ButtonToNavigate {...props} title="Navigate elsewhere" />} />
)    

const App = () => (
  <Router>
    <SomeComponent /> // Notice how in v4 we can have any other component interleaved
    <AnotherComponent />
  </Router>
);

2.- การใช้withRouterHoC

Routeองค์ประกอบการสั่งซื้อที่สูงขึ้นนี้จะฉีดอุปกรณ์ประกอบฉากเดียวกับ อย่างไรก็ตามมันมีข้อ จำกัด ที่คุณสามารถมีได้เพียง 1 HoC ต่อไฟล์

import { withRouter } from 'react-router-dom'

const ButtonToNavigate = ({ history }) => (
  <button
    type="button"
    onClick={() => history.push('/my-new-location')}
  >
    Navigate
  </button>
);


ButtonToNavigate.propTypes = {
  history: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired,
  }),
};

export default withRouter(ButtonToNavigate);

3.- การใช้Redirectส่วนประกอบ

การแสดงผล<Redirect>จะนำทางไปยังตำแหน่งใหม่ แต่โปรดทราบว่าตามค่าเริ่มต้นตำแหน่งปัจจุบันจะถูกแทนที่ด้วยตำแหน่งใหม่เช่นการเปลี่ยนเส้นทางฝั่งเซิร์ฟเวอร์ (HTTP 3xx) ตำแหน่งใหม่ถูกจัดเตรียมโดยtoprop ซึ่งอาจเป็นสตริง (URL เพื่อเปลี่ยนเส้นทาง) หรือlocationวัตถุ หากคุณต้องการที่จะผลักดันรายการใหม่เข้าสู่ประวัติศาสตร์แทนผ่านpushเสาเช่นกันและตั้งเป็นtrue

<Redirect to="/your-new-location" push />

4.- การเข้าถึงrouterด้วยตนเองผ่านบริบท

ท้อใจเล็กน้อยเนื่องจากบริบทยังคงเป็น API ทดลองและมีแนวโน้มที่จะแตก / เปลี่ยนแปลงใน React ในอนาคต

const ButtonToNavigate = (props, context) => (
  <button
    type="button"
    onClick={() => context.router.history.push('/my-new-location')}
  >
    Navigate to a new location
  </button>
);

ButtonToNavigate.contextTypes = {
  router: React.PropTypes.shape({
    history: React.PropTypes.object.isRequired,
  }),
};

ไม่จำเป็นต้องบอกว่ายังมีส่วนประกอบเราเตอร์อื่น ๆ ที่มีไว้สำหรับระบบนิเวศที่ไม่ใช่เบราว์เซอร์เช่น<NativeRouter>ที่ทำซ้ำการนำทางสแต็คในหน่วยความจำและเป้าหมายแพลตฟอร์มตอบโต้พื้นเมืองที่มีอยู่ผ่านreact-router-nativeแพคเกจ

สำหรับการอ้างอิงเพิ่มเติมใด ๆ ไม่ลังเลที่จะใช้เวลาดูที่เอกสารอย่างเป็นทางการ นอกจากนี้ยังมีวิดีโอที่สร้างโดยหนึ่งในผู้เขียนร่วมของไลบรารีที่ให้คำแนะนำเกี่ยวกับ react-router v4 ที่ยอดเยี่ยมโดยเน้นการเปลี่ยนแปลงที่สำคัญบางอย่าง


2
ฉันใช้ V4 และทำงานได้ดีกว่า ฉันใช้เวลาพอสมควรในการเข้าไปยุ่งเกี่ยวกับเราเตอร์ V4 เพราะดูเหมือนจะมีตัวเลือกแปลก ๆ แต่ข้างต้นใช้ได้ผลแน่นอน ฉันถือว่าคุณกำลังนำเข้าwithRouterจากreact-router-dom
Sam Parmenter

3
ฉันกำลังนำเข้าจากwithRouter react-router-domhistory.push เปลี่ยน url แต่ดูเหมือนจะไม่โหลด<Route>จนกว่าฉันจะรีเฟรชหน้า ...
BreakDS

1
@rauliyohmc ฉันผิดเกี่ยวกับเรื่องนี้ ปัญหาคือฉันมี<Router> องค์ประกอบภายใน React ที่ตกแต่งด้วย@observerซึ่งเป็นต้นเหตุของปัญหานี้ การแก้ไขคือการมี@withRouterส่วนประกอบ React ทุกอย่าง
BreakDS

3
สำหรับผู้ที่พบคำตอบที่ดีนี้withRouterเป็นเพียง HOC ที่ใช้Routeภายใต้ประทุน ซึ่งหมายความว่ามันใช้เพียง3 อุปกรณ์ประกอบฉากประวัติศาสตร์การแข่งขันและสถานที่ตั้ง ในตัวอย่างข้างต้นปรากฏว่าpushเป็นเสาที่withRouterจะเพิ่มButtonToNavigateแต่นั่นไม่ใช่กรณี props.history.pushจะต้องมีการใช้แทน หวังว่านี่จะช่วยให้ผู้อื่นที่สับสนเล็กน้อย
vinnymac

39
ว้าว. browserHistory.push('/path/some/where')ดูเหมือนจะง่ายกว่ามาก ผู้เขียนพยายามกีดกันการเขียนโปรแกรมที่จำเป็น แต่บางครั้งก็ใช้งานได้ดีกว่า!
lux

149

วิธีที่ง่ายที่สุดในการทำให้เสร็จ:

this.props.history.push("/new/url")

บันทึก:

  • คุณอาจต้องการส่งผ่านhistory propจากองค์ประกอบหลักลงไปยังส่วนประกอบที่คุณต้องการเรียกใช้การดำเนินการหากไม่มีอยู่

10
ฉันใช้เราเตอร์ 4.0 แต่ไม่มีคีย์ประวัติบนอุปกรณ์ประกอบฉาก ฉันจะแก้ไขได้อย่างไร
Nicolas S.Xu

41
หากคุณไม่ได้มีthis.props.historyอยู่ในองค์ประกอบของคุณแล้วคุณสามารถimport {withRouter} from 'react-router-dom'แล้ว export default withRouter(MyComponent)(หรือconst MyComponent = withRouter(...)) และมันจะแทรกhistoryรายการในอุปกรณ์ประกอบฉากขององค์ประกอบของคุณ
Malvineous

@Malvineous ที่น่าสนใจไม่ทราบเกี่ยวกับเรื่องนี้! จะลอง!
Jikku Jose

2
ฉันต้องคิดถึงบางสิ่งบางอย่างพื้นฐานเพราะมันใช้งานได้ดีมากสำหรับฉันดังนั้นฉันจึงไม่รู้ว่าทำไมคำตอบทั้งหมดต้องการคำอธิบายที่ยาวเช่นนี้
Joshua Pinter

วิธีการป้องกันส่วนประกอบที่ติดตั้งใหม่history.pushและเพียงแค่ทริกเกอร์การอัปเดตเช่นเมื่อเราคลิก<Link>
Rahul Yadav

55

ฉันมีปัญหาที่คล้ายกันเมื่อย้ายไปยัง React-Router v4 ดังนั้นฉันจะพยายามอธิบายวิธีแก้ปัญหาด้านล่าง

โปรดอย่าคิดว่าคำตอบนี้เป็นวิธีที่ถูกต้องในการแก้ปัญหาฉันคิดว่ามีโอกาสที่ดีที่จะเกิดอะไรขึ้นเมื่อ React Router v4 กลายเป็นผู้ใหญ่มากขึ้นและออกจากเบต้า (อาจมีอยู่แล้วและฉันไม่ได้ค้นพบมัน) .

สำหรับบริบทฉันมีปัญหานี้เพราะบางครั้งฉันใช้Redux-Sagaเพื่อเปลี่ยนวัตถุประวัติโดยทางโปรแกรม (พูดเมื่อผู้ใช้รับรองความถูกต้องสำเร็จ)

ในเอกสาร React Router ให้ดูที่<Router> ส่วนประกอบและคุณสามารถเห็นว่าคุณมีความสามารถในการส่งผ่านวัตถุประวัติของคุณเองผ่านเสา นี่คือสาระสำคัญของการแก้ปัญหา - เราจัดหาวัตถุประวัติศาสตร์ไปยังReact-Routerจากโมดูลทั่วโลก

ขั้นตอน:

  1. ติดตั้งโมดูลประวัติ npm - yarn add history หรือ npm install history --save
  2. สร้างไฟล์ที่เรียกว่าhistory.jsในApp.jsโฟลเดอร์ระดับของคุณ(นี่เป็นความชอบของฉัน)

    // src/history.js
    
    import createHistory from 'history/createBrowserHistory';
    export default createHistory();`
  3. เพิ่มวัตถุประวัตินี้ในองค์ประกอบเราเตอร์ของคุณเช่นนั้น

    // src/App.js
    
    import history from '../your/path/to/history.js;'
    <Router history={history}>
    // Route tags here
    </Router>
  4. ปรับ URL เหมือนเมื่อก่อนด้วยการนำเข้าวัตถุประวัติโลกของคุณ :

    import history from '../your/path/to/history.js;'
    history.push('new/path/here/');

ตอนนี้ทุกอย่างควรจะได้รับการซิงค์อยู่เสมอและคุณยังสามารถเข้าถึงวิธีการตั้งค่าวัตถุประวัติโดยทางโปรแกรมและไม่ใช่ผ่านส่วนประกอบ / คอนเทนเนอร์


5
การเปลี่ยนแปลงนี้ใช้ได้กับฉัน แต่เพียงเพราะฉันกำลังออกไปนอกส่วนประกอบ หากฉันนำทางภายในส่วนประกอบเช่น OP ฉันจะใช้วิธีที่แนะนำโดย @rauliyohmc โดยใช้อุปกรณ์ประกอบฉากที่ส่งผ่านโดยRouteส่วนประกอบ
Dan Ellis

2
นี่เป็นวิธีการที่แนะนำตั้งแต่ 08/17
Spets

2
@Spets ในกรณีของฉันหากฉันใช้วิธีการนี้ลิงก์จะได้รับการอัปเดตอย่างถูกต้องหลังจากการพุช แต่ส่วนประกอบไม่ได้รับการแสดงผลอย่างถูกต้อง (เช่นหลังจากอัปเดตลิงก์แล้วส่วนประกอบจะไม่ได้รับการอัปเดต คุณพบว่านี่เป็นแนวทางที่แนะนำ ลิงก์ใด ๆ / แหล่งที่มา?
เย็น

2
@ScottCoates ฉันแยกออกโดยใช้ตัวอย่างด้านบนแน่นอนโดยการให้ประวัติเป็นพารามิเตอร์ แต่หลังจากที่ฉันทำการดีบักของโมดูลโหนดด้วยตัวเอง มีข้อผิดพลาดทั่วไปเกิดขึ้นรอบ ๆ เว็บโดยใช้การนำเข้า "BrowserHistory เป็นเราเตอร์" เมื่อมีวัตถุอื่นที่เรียกว่าเราท์เตอร์ด้วยตัวเองในเวอร์ชั่นล่าสุดของ react-router-dom การใช้สิ่งนั้นร่วมกับประวัติที่สร้างในตัวอย่างด้านบนนั้นใช้งานได้ดี
เย็น

2
มีการอัปเดต url แต่หน้านั้นไม่ได้สร้างการแสดงผลโดยยึดตามรูตใหม่ ทางออกใด ๆ ทำไมเส้นทางทริกเกอร์จึงยาก คนที่มีปฏิกิริยาตอบสนองไม่ได้อยู่ในใจ
Nicolas S.Xu

43

TL; DR:

if (navigate) {
  return <Redirect to="/" push={true} />
}

คำตอบที่ง่ายและเปิดเผยคือคุณต้องใช้<Redirect to={URL} push={boolean} />ร่วมกับsetState()

push: บูลีน -เมื่อเป็นจริงการเปลี่ยนเส้นทางจะผลักรายการใหม่เข้าสู่ประวัติแทนการแทนที่รายการปัจจุบัน


import { Redirect } from 'react-router'

class FooBar extends React.Component {
  state = {
    navigate: false
  }

  render() {
    const { navigate } = this.state

    // here is the important part
    if (navigate) {
      return <Redirect to="/" push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

    return (
      <div>
        <button onClick={() => this.setState({ navigate: true })}>
          Home
        </button>
      </div>
    )
  }
}

ตัวอย่างเต็มรูปแบบที่นี่ อ่านเพิ่มเติมที่นี่

PS ตัวอย่างใช้ES7 + คุณสมบัติ Initializersเพื่อเริ่มต้นสถานะ ดูที่นี่เช่นกันหากคุณสนใจ


5
คำตอบนี้ควรได้รับการยอมรับ ทางออกที่สวยงามที่สุด! +1 สำหรับ @lustoykov
Ali Abbas Jaffri

1
ฉันยังตั้งค่าการนำทางกลับเป็นเท็จใน componentDidUpdate เนื่องจากปุ่มของฉันอยู่ในส่วนหัวและจะนำทางเพียงครั้งเดียวเท่านั้น
peterorum

ใช้งานได้เฉพาะเมื่อคุณทราบเกี่ยวกับการเปลี่ยนเส้นทางเมื่อโหลดหน้าเว็บ หากคุณกำลังรอการโทรแบบอะซิงโครนัสเพื่อส่งคืน (เช่นการตรวจสอบสิทธิ์กับ Google หรือบางอย่าง) คุณจะต้องใช้history.push()วิธีใดวิธีหนึ่ง
brittohalloran

ไม่จริงคุณยังสามารถใช้ประโยชน์จากลักษณะการประกาศของปฏิกิริยาที่รวมกับส่วนประกอบ <Redirect /> หากหน้าเว็บไม่โหลดคุณสามารถย้อนกลับไปที่ <Redirect /> อื่นได้
Lyubomir

@brittohalloran ความคิดของวิธีการนี้โดยใช้เราเตอร์ตอบสนองที่เหมาะสมคือเพื่อให้แน่ใจว่าคุณใช้ setState เพื่อบังคับให้แสดงผลอีกครั้ง
ใครบางคนพิเศษ

16

ใช้useHistoryhook หากคุณใช้ส่วนประกอบของฟังก์ชั่น

คุณสามารถใช้useHistoryhook เพื่อรับhistoryอินสแตนซ์

import { useHistory } from "react-router-dom";

const MyComponent = () => {
  var history = useHistory();

  return (
    <button onClick={() => history.push("/about")}>
      Click me
    </button>
  );
}

useHistoryเบ็ดช่วยให้คุณเข้าถึงอินสแตนซ์ประวัติศาสตร์ที่คุณอาจจะใช้เพื่อนำทาง

ใช้historyคุณสมบัติภายในส่วนประกอบของหน้า

React Router จะฉีดคุณสมบัติบางอย่างรวมhistoryถึงส่วนประกอบของหน้า

class HomePage extends React.Component {
  render() {
    const { history } = this.props;

    return (
      <div>
        <button onClick={() => history.push("/projects")}>
          Projects
        </button>
      </div>
    );
  }
}

ล้อมรอบองค์ประกอบลูกwithRouterเพื่อฉีดคุณสมบัติเราเตอร์

withRouterwrapper ฉีดคุณสมบัติของเราเตอร์ไปยังส่วนประกอบ ตัวอย่างเช่นคุณสามารถใช้ wrapper นี้เพื่อฉีดเราเตอร์ไปยังส่วนประกอบปุ่มออกจากระบบที่อยู่ในเมนูผู้ใช้

import { withRouter } from "react-router";

const LogoutButton = withRouter(({ history }) => {
  return (
    <button onClick={() => history.push("/login")}>
      Logout
    </button>
  );
});

export default LogoutButton;

11

คุณสามารถใช้อุปกรณ์ประกอบฉากเพื่อเข้าถึงวัตถุประวัติ: this.props.history.push('new_url')


5
มีประโยชน์เฉพาะเมื่ออยู่ในส่วนประกอบที่สืบเชื้อสายมาจากเราเตอร์โดยตรง เกรงว่าคุณจะผ่านประวัติศาสตร์ไปสู่ทุก ๆ ส่วนที่คุณต้องการใช้ฟังก์ชั่นนี้
mwieczorek

3
หากคุณไม่ได้มีthis.props.historyอยู่ในองค์ประกอบของคุณแล้วคุณสามารถimport {withRouter} from 'react-router-dom'แล้ว export default withRouter(MyComponent)(หรือconst MyComponent = withRouter(...)) และมันจะแทรกhistoryรายการในอุปกรณ์ประกอบฉากขององค์ประกอบของคุณ
Malvineous

9

ขั้นตอนที่ 1: มีสิ่งเดียวเท่านั้นที่จะนำเข้าด้านบน:

import {Route} from 'react-router-dom';

ขั้นตอนที่ 2: ในเส้นทางของคุณผ่านประวัติศาสตร์:

<Route
  exact
  path='/posts/add'
  render={({history}) => (
    <PostAdd history={history} />
  )}
/>

ขั้นตอนที่ 3: ประวัติได้รับการยอมรับว่าเป็นส่วนหนึ่งของอุปกรณ์ประกอบฉากในคอมโพเนนต์ถัดไปดังนั้นคุณสามารถ:

this.props.history.push('/');

นั่นง่ายและทรงพลังจริงๆ


7

งานนี้:

import { withRouter } from 'react-router-dom';

const SomeComponent = withRouter(({ history }) => (
    <div onClick={() => history.push('/path/some/where')}>
        some clickable element
    </div>); 
);

export default SomeComponent;

5

คำตอบของฉันมีความคล้ายคลึงกับอเล็กซ์ ฉันไม่แน่ใจว่าทำไม React-Router จึงทำให้สิ่งนี้ซับซ้อนโดยไม่จำเป็น ทำไมฉันต้องห่อส่วนประกอบของฉันกับ HoC เพื่อเข้าถึงสิ่งที่ทั่วโลกต้องการ

อย่างไรก็ตามถ้าคุณดูที่วิธีที่พวกเขาดำเนินการ<BrowserRouter>ก็แค่กระดาษห่อเล็ก ๆ รอบประวัติศาสตร์

เราสามารถดึงบิตประวัติออกมาเพื่อให้เราสามารถนำเข้าได้จากทุกที่ อย่างไรก็ตามเคล็ดลับคือถ้าคุณทำการเรนเดอร์ฝั่งเซิร์ฟเวอร์และคุณลองโมดูimportประวัติมันจะไม่ทำงานเพราะใช้ API เฉพาะเบราว์เซอร์ แต่ก็ไม่เป็นไรเพราะปกติแล้วเราจะเปลี่ยนเส้นทางเพื่อตอบสนองต่อการคลิกหรือเหตุการณ์ฝั่งไคลเอ็นต์อื่น ๆ ดังนั้นจึงอาจตกลงที่จะปลอมมัน:

// history.js
if(__SERVER__) {
    module.exports = {};
} else {
    module.exports = require('history').createBrowserHistory();
}

ด้วยความช่วยเหลือของ webpack เราสามารถกำหนด vars บางอย่างเพื่อให้เรารู้ว่าเราอยู่ในสภาพแวดล้อมใด:

plugins: [
    new DefinePlugin({
        '__SERVER__': 'false',
        '__BROWSER__': 'true', // you really only need one of these, but I like to have both
    }),

และตอนนี้คุณสามารถ

import history from './history';

จากที่ใดก็ได้ มันจะส่งคืนโมดูลเปล่าบนเซิร์ฟเวอร์

หากคุณไม่ต้องการใช้เวทย์มนตร์เหล่านี้คุณจะต้องทำสิ่งที่จำเป็นrequireในพื้นที่ส่วนกลาง (ภายในตัวจัดการเหตุการณ์ของคุณ) importจะไม่ทำงานเพราะใช้งานได้ในระดับสูงสุดเท่านั้น


6
แช่งพวกเขาทำให้มันซับซ้อนมาก
Abhishek

4
ฉันเห็นด้วยกับคุณโดยสิ้นเชิง นี่ซับซ้อนมากสำหรับการนำทาง
Thai Tran

5

ฉันคิดว่า @rgommezz ครอบคลุมกรณีส่วนใหญ่ลบหนึ่งที่ฉันคิดว่ามันค่อนข้างสำคัญ

// history is already a dependency or React Router, but if don't have it then try npm install save-dev history

import createHistory from "history/createBrowserHistory"

// in your function then call add the below 
const history = createHistory();
// Use push, replace, and go to navigate around.
history.push("/home");

สิ่งนี้ทำให้ฉันสามารถเขียนบริการง่าย ๆ พร้อมการดำเนินการ / การโทรที่ฉันสามารถเรียกใช้การนำทางจากส่วนประกอบใด ๆ ที่ฉันต้องการโดยไม่ต้องทำ HoC มากในส่วนประกอบของฉัน ...

ยังไม่ชัดเจนว่าทำไมไม่มีใครให้บริการโซลูชั่นนี้มาก่อน ฉันหวังว่ามันจะช่วยได้และหากคุณพบปัญหาใด ๆ โปรดแจ้งให้เราทราบ


4

ฉันทดสอบ v4 มาสองสามวันแล้วและฉันก็ชอบมันมาก! มันทำให้รู้สึกหลังจากที่ในขณะ

ฉันยังมีคำถามเดียวกันและฉันพบว่าการจัดการเช่นนี้ทำงานได้ดีที่สุด (และอาจเป็นไปตามที่ตั้งใจไว้) มันใช้สถานะผู้ประกอบการที่ประกอบ<Redirect>ไปด้วยและ

ในตัวสร้าง ()

this.state = {
    redirectTo: null
} 
this.clickhandler = this.clickhandler.bind(this);

ในเรนเดอร์ ()

render(){
    return (
        <div>
        { this.state.redirectTo ?
            <Redirect to={{ pathname: this.state.redirectTo }} /> : 
            (
             <div>
               ..
             <button onClick={ this.clickhandler } />
              ..
             </div>
             )
         }

ใน clickhandler ()

 this.setState({ redirectTo: '/path/some/where' });

หวังว่ามันจะช่วย แจ้งให้เราทราบ


มีข้อผิดพลาดใด ๆ กับวิธีนี้หรือไม่? ในโครงการของฉันที่มีการทำงานเฉพาะเมื่อฉันตั้งค่าสถานะในตัวสร้างจากนั้นฉันสามารถเปลี่ยนเส้นทางไปยังหน้าใด ๆ ที่ฉันต้องการ แต่เมื่อฉันตั้งค่าสถานะเหตุการณ์ (อุปกรณ์เปลี่ยนตัวอย่างเช่น) ฉันเห็นวิธีการแสดงผลที่เรียกว่าด้วยสถานะใหม่ แต่การเปลี่ยนเส้นทางไม่เกิดขึ้นฉันเห็นหน้าเดียวกัน
Dmitry Malugin

ข้อผิดพลาดคือมันจะเข้ามาแทนที่ประวัติศาสตร์ดังนั้นคุณจะไม่สามารถตีกลับได้ - โดยทั่วไปนี่ไม่ใช่ทางออกที่ดี
dannytenaglias

4

ฉันดิ้นรนกับสิ่งนี้มาระยะหนึ่ง - บางอย่างเรียบง่าย แต่ซับซ้อนเพราะ ReactJS เป็นวิธีที่แตกต่างอย่างสิ้นเชิงในการเขียนเว็บแอปพลิเคชัน

ฉันสร้างองค์ประกอบแยกต่างหากเพื่อทำให้ความยุ่งเหยิงเป็นนามธรรม

// LinkButton.js

import React from "react";
import PropTypes from "prop-types";
import {Route} from 'react-router-dom';

export default class LinkButton extends React.Component {

    render() {
        return (
            <Route render={({history}) => (
                <button {...this.props}
                       onClick={() => {
                           history.push(this.props.to)
                       }}>
                    {this.props.children}
                </button>
            )}/>
        );
    }
}

LinkButton.propTypes = {
    to: PropTypes.string.isRequired
};

จากนั้นเพิ่มลงในrender()วิธีการของคุณ:

<LinkButton className="btn btn-primary" to="/location">
    Button Text
</LinkButton>

ฉันพบว่าวิธีนี้มีประโยชน์มาก ฉันกำลังคัดลอกรหัส แจ้งให้เราทราบว่าคุณใส่มันไว้ใน GitHub - ฉันจะบอกคุณโดยตรง
Abhinav Manchanda

3

เนื่องจากไม่มีวิธีอื่นที่จะจัดการกับการออกแบบที่น่ากลัวนี้ฉันจึงเขียนส่วนประกอบทั่วไปที่ใช้วิธีwithRouter HOC ตัวอย่างด้านล่างคือการห่อbuttonองค์ประกอบ แต่คุณสามารถเปลี่ยนเป็นองค์ประกอบที่คลิกได้ที่คุณต้องการ:

import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';

const NavButton = (props) => (
  <Button onClick={() => props.history.push(props.to)}>
    {props.children}
  </Button>
);

NavButton.propTypes = {
  history: PropTypes.shape({
    push: PropTypes.func.isRequired
  }),
  to: PropTypes.string.isRequired
};

export default withRouter(NavButton);

การใช้งาน:

<NavButton to="/somewhere">Click me</NavButton>

3
this.props.history.push("/url")

หากคุณไม่พบ this.props.history พร้อมใช้งานในคอมโพเนนต์ของคุณให้ลองสิ่งนี้

import {withRouter} from 'react-router-dom'
export default withRouter(MyComponent)  

ขอบคุณมาก!
QauseenMZ

1

เนื่องจากบางครั้งฉันต้องการเปลี่ยนเส้นทางด้วย Application จากนั้นกดปุ่มนี่เป็นตัวอย่างการทำงานที่น้อยที่สุดสำหรับฉัน:

import { Component } from 'react'
import { BrowserRouter as Router, Link } from 'react-router-dom'

class App extends Component {
  constructor(props) {
    super(props)

    /** @type BrowserRouter */
    this.router = undefined
  }

  async handleSignFormSubmit() {
    await magic()
    this.router.history.push('/')
  }

  render() {
    return (
      <Router ref={ el => this.router = el }>
        <Link to="/signin">Sign in</Link>
        <Route path="/signin" exact={true} render={() => (
          <SignPage onFormSubmit={ this.handleSignFormSubmit } />
        )} />
      </Router>
    )
  }
}

0

สำหรับบรรดาของคุณที่ต้องการเปลี่ยนเส้นทางก่อนที่จะ initalizing เราเตอร์โดยใช้อย่างเต็มที่React RouterหรือReact Router Domคุณสามารถให้การเปลี่ยนเส้นทางโดยเพียงแค่ accesing วัตถุประวัติศาสตร์และผลักดันสถานะใหม่บนมันภายในโครงสร้างของapp.jsคุณ พิจารณาสิ่งต่อไปนี้:

function getSubdomain(hostname) {
    let regexParse = new RegExp('[a-z\-0-9]{2,63}\.[a-z\.]{2,5}$');
    let urlParts = regexParse.exec(hostname);
    return hostname.replace(urlParts[0], '').slice(0, -1);
}

class App extends Component {

    constructor(props) {
        super(props);


        this.state = {
            hostState: true
        };

        if (getSubdomain(window.location.hostname).length > 0) {
            this.state.hostState = false;
            window.history.pushState('', '', './login');
        } else {
            console.log(getSubdomain(window.location.hostname));
        }

    }


    render() {
        return (

            <BrowserRouter>
                {this.state.hostState ? (
                    <div>
                        <Route path="/login" component={LoginContainer}/>
                        <Route path="/" component={PublicContainer}/>
                    </div>
                ) : (
                    <div>
                        <Route path="/login" component={LoginContainer}/>
                    </div>
                )

                }
            </BrowserRouter>)
    }


}

ที่นี่เราต้องการเปลี่ยนเส้นทางเอาต์พุตขึ้นอยู่กับโดเมนย่อยโดยการโต้ตอบกับวัตถุประวัติก่อนที่ส่วนประกอบจะแสดงผลเราสามารถเปลี่ยนเส้นทางได้อย่างมีประสิทธิภาพในขณะที่ยังคงเส้นทางของเราอยู่ในชั้นเชิง

window.history.pushState('', '', './login');
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.