React 路由器更改 url 但未更新上下文
Posted
技术标签:
【中文标题】React 路由器更改 url 但未更新上下文【英文标题】:React router changes url but not context is not updated 【发布时间】:2020-04-17 16:29:08 【问题描述】:我有一个页面模板,其中包含一组不同的键,用于以用户选择的语言显示文本。默认语言为英文,英文页面对应的URL为localhost:3000
。用户可以选择通过单击按钮切换到西班牙语。当用户单击按钮以切换到西班牙语时,将调用 history.push()
函数。它使用给定的国家代码更新 URL,在这种情况下 URL 变为 localhost:3000/es
。单击浏览器后退按钮时,URL 会变回 localhost:3000
,但视图仍为西班牙语。
我在这个问题上苦苦挣扎了一段时间,并尝试实施多种解决方案,其中包括:
使用WithRouter
组件包装组件;
使用Router
组件将历史对象作为道具传递;
使用componentDidUpdate
强制更新组件;
App
组件中的调用调度;
我的index.js
文件包含渲染AppWrapper
组件的路由。 AppWrapper
组件呈现 App
组件,它是 I18nContextProvider
组件的子组件。
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import BrowserRouter, Route, Switch from 'react-router-dom';
import App from './App';
import I18nContextProvider, availableTranslationsEnum from './languages/languageHandler';
const AppWrapper = (language) => (
<I18nContextProvider language=language>
<App />
</I18nContextProvider>
);
const appRoutes = (
<BrowserRouter>
<Switch>
<Route
path=`/$availableTranslationsEnum.spanish`
render=() => AppWrapper(availableTranslationsEnum.spanish)
exact
/>
<Route
render=() => AppWrapper(availableTranslationsEnum.english)
/>
</Switch>
</BrowserRouter>
);
ReactDOM.render(appRoutes, document.getElementById('root'));
I18nContextProvider
组件在 languageHandler.js
文件中定义,如下所示。
//languageHandler.js
import React, useReducer from 'react';
import EN from './en.json';
import ES from './es.json';
const translations =
en: EN,
es: ES,
;
const getTranslate = (langCode) => (key) => translations[langCode][key] || key;
export const availableTranslationsEnum =
english: Object.keys(translations)[0],
spanish: Object.keys(translations)[1],
;
const setInitialState = (language) =>
const defaultLanguage = availableTranslationsEnum.english;
const siteLanguage = language || defaultLanguage;
return
langCode: siteLanguage,
translate: getTranslate(siteLanguage),
;
;
export const I18nContext = React.createContext(setInitialState());
export const I18nContextProvider = ( children, language ) =>
const reducer = (action) =>
switch (action.type)
case 'setLanguage':
return
langCode: action.payload,
translate: getTranslate(action.payload),
;
default:
return ...setInitialState(language) ;
;
const [state, dispatch] = useReducer(reducer, setInitialState(language));
return (
<I18nContext.Provider value= ...state, dispatch >
children
</I18nContext.Provider>
);
;
App
组件包含翻译键,如下所示。
//App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import I18nContext from './languages/languageHandler';
import LanguageSelect from './components/LanguageSelect';
const App = () =>
const translate = React.useContext(I18nContext);
return (
<div className="App">
<header className="App-header">
<img src=logo className="App-logo" />
<LanguageSelect />
<p>
translate('edit_and_save')
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
translate('learn_react')
</a>
</header>
</div>
);
;
export default App;
App
组件渲染LanguageSelect
组件,看起来像这样。
//LanguageSelect.js
import React, useContext from 'react';
import useHistory from 'react-router-dom';
import I18nContext, availableTranslationsEnum from '../languages/languageHandler';
const LanguageSelect = () =>
const history = useHistory();
const langCode, dispatch = useContext(I18nContext);
const switchLanguage = (languageToSwitchTo) =>
const dispatchType = 'setLanguage';
dispatch( type: dispatchType, payload: languageToSwitchTo );
if (languageToSwitchTo !== availableTranslationsEnum.english)
history.push(`/$languageToSwitchTo`);
if (languageToSwitchTo === availableTranslationsEnum.english)
history.push(`/`); //Default language
;
if (langCode === availableTranslationsEnum.english)
return (
<button onClick=() => switchLanguage(availableTranslationsEnum.spanish)>Español</button>
);
return (<button onClick=() => switchLanguage(availableTranslationsEnum.english)>English</button>);
;
export default LanguageSelect;
我试图了解为什么 URL 发生了变化,但组件保持原样。如果有人可以帮助我将我推向正确的方向,我们将不胜感激。
【问题讨论】:
【参考方案1】:使用 react 开发工具检查组件的 props 和 states - 无需更改,无需重新渲染。
您的减速器适用于“事件”...更改 url(后退按钮)会导致 prop 更改,而不是动作调度。
<I18nContext.Provider>
使用状态,而不是道具 - 在道具更改时保持不变,没有理由重新渲染。
只需检查道具并更新渲染函数以了解道具更改。
【讨论】:
【参考方案2】:将您的语言值添加到 basename 属性到 <BrowserRouter>
组件,它将为您的所有路由创建一个基本路径。
const appRoutes = props => (
<BrowserRouter basename=props.language>
<I18nContextProvider language=props.language>
<App />
</I18nContextProvider>
</BrowserRouter>
);
如果语言是英语,那么您的网址将是:
localhost:3000/en
对于您拥有的其他语言并且基本名称不会改变。由用户使用 push 功能。
【讨论】:
你能再解释一下吗?一旦用户单击浏览器的后退按钮,如何通过语言的值以及添加基本名称来更新组件? 无需检查返回按钮,只需将应用组件连接到商店以获取所选语言并将其作为基本名称添加到 BrowserRoute 组件。 将我的代码更改为<BrowserRouter basename=props.language><I18nContextProvider language =props.language><App /></I18nContextProvider> </BrowserRouter>
时,会引发错误TypeError: Cannot read property edit_and_save of undefined
在languageHandler.js
中引用getTranslate()
。以上是关于React 路由器更改 url 但未更新上下文的主要内容,如果未能解决你的问题,请参考以下文章