回答:
私withRouter
はlocation
小道具を手に入れるのに使います。新しいルートのためにコンポーネントが更新されたら、値が変更されたかどうかを確認します。
@withRouter
class App extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired
}
// ...
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
// ...
render(){
return <Switch>
<Route path="/" exact component={HomePage} />
<Route path="/checkout" component={CheckoutPage} />
<Route path="/success" component={SuccessPage} />
// ...
<Route component={NotFound} />
</Switch>
}
}
それが役に立てば幸い
withRouter
ていますが、エラーが発生していますYou should not use <Route> or withRouter() outside a <Router>
。<Router/>
上記のコードにはコンポーネントがありません。それで、それはどのように機能していますか?
<Switch>
コンポーネントは事実上のルーターとして機能しています。<Route>
一致するパスを持つ最初のエントリのみがレンダリングされます。<Router/>
このシナリオではコンポーネントは必要ありません
上記を拡張するには、履歴オブジェクトを取得する必要があります。を使用している場合は、コンポーネントをBrowserRouter
インポートwithRouter
して高次コンポーネント(HoC)でラップし、propsを介して履歴オブジェクトのプロパティと関数にアクセスできます。
import { withRouter } from 'react-router-dom';
const myComponent = ({ history }) => {
history.listen((location, action) => {
// location is an object like window.location
console.log(action, location.pathname, location.state)
});
return <div>...</div>;
};
export default withRouter(myComponent);
知っておくべき唯一のことは、withRouterや他のほとんどの方法でhistory
オブジェクトにアクセスして、オブジェクトを構造化解除してプロップを汚染するように見えることです。
withRoutes
してくださいwithRouter
。
履歴v4 lib を使用する必要があります。
そこからの例
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
history.push
はトリガーを行うようですhistory.listen
。履歴v4ドキュメントのベースURLの使用例をご覧ください。これは実際にはブラウザのネイティブオブジェクトのラッパーであるため、ネイティブオブジェクトとまったく同じようには動作しません。history
history
const unlisten = history.listen(myListener); unlisten();
withRouter
、history.listen
、およびuseEffect
(フックを反応させるのは)一緒に非常にうまく動作します:
import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
const Component = ({ history }) => {
useEffect(() => history.listen(() => {
// do something on route change
// for my example, close a drawer
}), [])
//...
}
export default withRouter(Component)
リスナーコールバックは、ルートが変更されるたびに発生history.listen
しuseEffect
ます。戻り値は、でうまく機能するシャットダウンハンドラーです。
v5.1には便利なフックが導入されています useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselocation
import { Switch, useLocation } from 'react-router-dom'
function usePageViews() {
let location = useLocation()
useEffect(
() => {
ga.send(['pageview', location.pathname])
},
[location]
)
}
function App() {
usePageViews()
return <Switch>{/* your routes here */}</Switch>
}
Cannot read property 'location' of undefined at useLocation
。ルータをツリーに配置するコンポーネントと同じコンポーネントにuseLocation()コールが含まれていないことを確認する必要があります。こちらを参照してください
フック付き:
import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'
const DebugHistory = ({ history }) => {
useEffect(() => {
console.log('> Router', history.action, history.location])
}, [history.location.key])
return null
}
DebugHistory.propTypes = { history: historyShape }
export default withRouter(DebugHistory)
<DebugHistory>
コンポーネントとしてインポートしてレンダリングする
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';
function MyApp() {
const location = useLocation();
useEffect(() => {
console.log('route has been changed');
...your code
},[location.pathname]);
}
フック付き
反応フックで、私は使用しています useEffect
const history = useHistory()
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
const [search, setSearch] = useState(parsed.search ? parsed.search : '')
useEffect(() => {
const parsedSearch = parsed.search ? parsed.search : ''
if (parsedSearch !== search) {
// do some action! The route Changed!
}
}, [location.search])
次のrender
ようにcomponent
、の代わりに属性を使用する場合があります。
class App extends React.Component {
constructor (props) {
super(props);
}
onRouteChange (pageId) {
console.log(pageId);
}
render () {
return <Switch>
<Route path="/" exact render={(props) => {
this.onRouteChange('home');
return <HomePage {...props} />;
}} />
<Route path="/checkout" exact render={(props) => {
this.onRouteChange('checkout');
return <CheckoutPage {...props} />;
}} />
</Switch>
}
}
onRouteChange
メソッドの状態を変更すると、「最大更新深度を超えました」エラーが発生する可能性があることに注意してください。
useEffect
フックそれがリスナーを追加することなく、ルート変更を検出することが可能です。
import React, { useEffect } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import Main from './Main';
import Blog from './Blog';
const App = ({history}) => {
useEffect( () => {
// When route changes, history.location.pathname changes as well
// And the code will execute after this line
}, [history.location.pathname]);
return (<Switch>
<Route exact path = '/' component = {Main}/>
<Route exact path = '/blog' component = {Blog}/>
</Switch>);
}
export default withRouter(App);
私はこの問題に対処しただけなので、与えられた他の回答の補足として私のソリューションを追加します。
ここでの問題はuseEffect
、呼び出しが最初のレンダリング後にのみトリガーされるため、意図したとおりに機能しないため、不要な遅延が発生することです。
reduxなどの状態マネージャーを使用している場合、ストア内の状態が長引いているため、画面にちらつきが発生する可能性があります。
useLayoutEffect
これはすぐにトリガーされるので、本当に使用したいのは使用することです。
そこで、ルーターと同じディレクトリに配置する小さなユーティリティ関数を作成しました。
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
これをコンポーネントHOC内から次のように呼び出します。
callApis(() => getTopicById({topicId}), path);
path
match
を使用するときにオブジェクトに渡される小道具ですwithRouter
。
私は実際に手動でヒストリを聞いたり聞いたりすることを好みません。それはただのイモです。