msh*_*eer 32 javascript reactjs
我在上下文中有语言设置,如下所示
class LanguageProvider extends Component {
static childContextTypes = {
langConfig: PropTypes.object,
};
getChildContext() {
return { langConfig: 'en' };
}
render() {
return this.props.children;
}
}
export default LanguageProvider;
Run Code Online (Sandbox Code Playgroud)
我的应用程序代码如下所示
<LanguageProvider>
<App>
<MyPage />
</App>
</LanguageProvider>
Run Code Online (Sandbox Code Playgroud)
我的页面有一个用于切换语言的组件
<MyPage>
<LanguageSwitcher/>
</MyPage>
Run Code Online (Sandbox Code Playgroud)
LanguageSwitcher 在此MyPage需要更新上下文以将语言更改为'jp',如下所示
class LanguageSwitcher extends Component {
static contextTypes = {
langConfig: PropTypes.object,
};
updateLanguage() {
//Here I need to update the langConfig to 'jp'
}
render() {
return <button onClick={this.updateLanguage}>Change Language</button>;
}
}
export default LanguageSwitcher;
Run Code Online (Sandbox Code Playgroud)
如何从LanguageSwitcher组件内部更新上下文?
Div*_*ani 69
注意:我现在使用最新的上下文API,使用React 16.3,它提供了一种动态上下文的好方法.CodeSandbox演示
首先,为了获得可以传递给消费者的动态上下文,我将使用父级的状态.这确保我有一个单一的真理来源.例如,我的父App将如下所示:
class App extends Component {
setLanguage = language => {
this.setState({ language });
};
state = {
language: "en",
setLanguage: this.setLanguage
};
...
}
Run Code Online (Sandbox Code Playgroud)
它language与语言设置器方法一起存储在状态中,您可以将其保存在状态树之外.
接下来,我创建了一个这样的语言上下文:
// set the defaults
const LanguageContext = React.createContext({
language: "en",
setLanguage: () => {}
});
Run Code Online (Sandbox Code Playgroud)
这里我设置language('en')的默认值和一个setLanguage将由上下文提供程序发送给消费者的函数.这些只是默认值,我将在父级中使用提供程序组件时提供它们的值App.
为了让语言切换器设置语言,它应该可以通过上下文访问语言设置器功能.它看起来像这样:
class LanguageSwitcher extends Component {
render() {
return (
<LanguageContext.Consumer>
{({ language, setLanguage }) => (
<button onClick={() => setLanguage("jp")}>
Switch Language (Current: {language})
</button>
)}
</LanguageContext.Consumer>
);
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我只是将语言设置为'jp',但您可能有自己的逻辑来为此设置语言.
现在我将把我的语言切换器组件渲染到一个LanguageContext.Provider并将必须通过上下文发送的值传递到更深层次的任何级别.这是我父母的App样子:
class App extends Component {
setLanguage = language => {
this.setState({ language });
};
state = {
language: "en",
setLanguage: this.setLanguage
};
render() {
return (
<LanguageContext.Provider value={this.state}>
<h2>Current Language: {this.state.language}</h2>
<p>Click button to change to jp</p>
<div>
{/* Can be nested */}
<LanguageSwitcher />
</div>
</LanguageContext.Provider>
);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,只要单击语言切换器,它就会动态更新上下文.
Mat*_*ani 23
Maithani的上述答案是一个很好的解决方案,但这是不使用钩子的类组件。由于React建议使用功能组件和挂钩,因此我将使用useContext和useState挂钩实现它。这是从子组件中更新上下文的方法。
LanguageContextMangement.js
import React, { useState } from 'react'
export const LanguageContext = React.createContext({
language: "en",
setLanguage: () => {}
})
export const LanguageContextProvider = (props) => {
const setLanguage = (language) => {
setState({...state, language: language})
}
const initState = {
language: "en",
setLanguage: setLanguage
}
const [state, setState] = useState(initState)
return (
<LanguageContext.Provider value={state}>
{props.children}
</LanguageContext.Provider>
)
}
Run Code Online (Sandbox Code Playgroud)
App.js
import React, { useContext } from 'react'
import { LanguageContextProvider, LanguageContext } from './LanguageContextManagement'
function App() {
const state = useContext(LanguageContext)
return (
<LanguageContextProvider>
<button onClick={() => state.setLanguage('pk')}>
Current Language is: {state.language}
</button>
</LanguageContextProvider>
)
}
export default App
Run Code Online (Sandbox Code Playgroud)
Nic*_*ton 20
我个人喜欢这种模式:
文件:context.jsx
import React from 'react';
// The Context
const TemplateContext = React.createContext();
// Template Provider
const TemplateProvider = ({children}) => {
const [myValue, setMyValue] = React.useState(0);
// Context values passed to consumer
const value = {
myValue, // <------ Expose Value to Consumer
setMyValue // <------ Expose Setter to Consumer
};
return (
<TemplateContext.Provider value={value}>
{children}
</TemplateContext.Provider>
)
}
// Template Consumer
const TemplateConsumer = ({children}) => {
return (
<TemplateContext.Consumer>
{(context) => {
if (context === undefined) {
throw new Error('TemplateConsumer must be used within TemplateProvider');
}
return children(context)
}}
</TemplateContext.Consumer>
)
}
// useTemplate Hook
const useTemplate = () => {
const context = React.useContext(TemplateContext);
if(context === undefined)
throw new Error('useTemplate must be used within TemplateProvider');
return context;
}
export {
TemplateProvider,
TemplateConsumer,
useTemplate
}
Run Code Online (Sandbox Code Playgroud)
然后您可以创建一个功能组件(如果它是提供程序树中的子组件):
文件:组件.jsx
import React from 'react';
import {useTemplate} from 'context.jsx';
const MyComponent = () => {
// Get the value and setter from the consumer hook
const {myValue, setMyValue} = useTemplate();
// Demonstrate incrementing the value
React.useEffect(() => {
// Increment, set in context
const increment = () => setMyValue(prev => prev + 1);
// Increment every second
let interval = setInterval(increment, 1000);
// Cleanup, kill interval when unmounted
return () => clearInterval(interval);
},[]) // On mount, no dependencies
// Render the value as it is pulled from the context
return (
<React.Fragment>
Value of MyValue is: {myValue}
</React.Fragment>
)
}
Run Code Online (Sandbox Code Playgroud)
这是我的方法,基于@Nicholas Hamilton 的答案,但适用于 TypeScript 并应用@LIIT 建议。
注意:我正在使用 Next.js 和高度固执己见的 ESLint 设置。
import { createContext, useContext, useMemo, useState, Dispatch, SetStateAction } from "react"
interface TemplateContextProps {
myValue: number | null
setMyValue: Dispatch<SetStateAction<number | null>>
}
const TemplateContext = createContext<TemplateContextProps>({
myValue: null,
setMyValue: (prevState: SetStateAction<number | null>) => prevState,
})
interface TemplateProviderProps {
children: React.ReactNode
}
function TemplateProvider({ children }: TemplateProviderProps): JSX.Element {
const [myValue, setMyValue] = useState<number | null>(null)
const value = useMemo(() => ({ myValue, setMyValue }), [myValue, setMyValue])
return <TemplateContext.Provider value={value}>{children}</TemplateContext.Provider>
}
const TemplateConsumer = TemplateContext.Consumer
const useTemplate = () => useContext(TemplateContext)
export { TemplateProvider, TemplateConsumer, useTemplate }
Run Code Online (Sandbox Code Playgroud)
我喜欢将值初始化为 null,这是一种更动态的方法,但您可以将类型限制为仅数字并将其默认设置为 0。