如何从多个组件更新反应上下文?

ans*_*eva 1 javascript reactjs react-context react-hooks use-context

所以我想了解一下,React contexts但我有点困惑。从它的文档:

Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动向下传递 props。

所以这意味着我可以将应用程序的整个状态设为全局,并且可以从任何子组件更新它,对吧?但是我对如何使用它感到困惑。我有一个小应用程序,可以根据用户的输入向用户显示登录、注册或登录屏幕。我期望以下任何组件都应该能够更改存储在上下文中的全局对象的值,但我不确定如何使用它(提到不确定的函数todos

// context
const MyAppSettings = React.createContext(
    {
        userId:null,
        enableMarketing:false,
        theme:"light"
    }
)
Run Code Online (Sandbox Code Playgroud)
//ui components(having access to local state as well as global context

function SettingsUI({onThemeChange,onConsentChange}){
    let settings = useContext(MyAppSettings)

    return(
        <div>
            <button onClick={e=>onThemeChange()}>Change Theme to {settings.theme==="light"?"dark":"light"}</button>
            <br/>
            <button onClick={e=>onConsentChange()}> {settings.enableMarketing?"withdraw consent for marketing emails":"give consent for marketing emails"}</button>
        </div>
    )


}
function Auth({onAuthClick}){
    let settings = useContext(MyAppSettings)
    let textColor = settings.theme==="light" ? "black" : "white"
    let bg = settings.theme==="light"?"white": "brown"
    let finalStyling= {border:"1px solid black",width:"200px",display:"block",marginBottom:"4px",backgroundColor:bg,color:textColor}

    let [currentEmail,updateEmail] = useState("")
    let emailUI =  <input type="text" style={finalStyling} placeholder="email" value={currentEmail} onChange={e=>updateEmail(e.target.value)} />

    let [currentPwd,updatePwd] = useState("")
    let passwordUi =  <input type="password" style={finalStyling} placeholder="password" value={currentPwd} onChange={e=>updatePwd(e.target.value)}  />

    let [currentName,updateName] = useState("")
    let [isSignup,toggleSignUp ]= useState(false)
    let nameUi =  isSignup ? <input type="text" style={finalStyling} placeholder="name"  value={currentName} onChange={e=>updateName(e.target.value)} /> :  ""

    let authBtnText = isSignup? "Sign up now!" : "Login now!"
    let switchBtnText = isSignup? "Login Instead" : "Signup Instead"

    function getCurrentInfo(){
        return {
            email:currentEmail,
            pwd:currentPwd,
            isUserSigningUp:isSignup,
            name:currentName
        }
    }

    return(
        <>
            {nameUi}
            {emailUI}
            {passwordUi}
            <div>
                <button onClick={e=>onAuthClick(getCurrentInfo())} >{authBtnText}</button>
                <button onClick={e=>toggleSignUp(!isSignup)} >{switchBtnText}</button>
            </div>
        </>
    )
}
function LoggedIn({logoutClick}){
    let settings = useContext(MyAppSettings)
    let textColor = settings.theme === "light" ? "black" : "white"
    let bg = settings.theme === "light" ? "white" : "brown"
    return (
        <div style={{padding: "8px", backgroundColor: bg, color: textColor}}>
            <p>You have successfully logged in. your user id is {settings.userId}</p>
            <button onClick={e => logoutClick()}>logout</button>
        </div>
    )
}
Run Code Online (Sandbox Code Playgroud)

//component controlling  the other components and responsible for changing context values (TODO: HOW??)

function UserLoginSystem(){
    let settings = useContext(MyAppSettings)

    let onThemeChangeListener = ()=> {/*todo toggle theme settings to dark/light*/}
    let onConsentChangeListener = ()=> {/*todo toggle theme consent settings*/}

    let section1 = <SettingsUI onConsentChange={onConsentChangeListener} onThemeChange={onThemeChangeListener}/>


    let onUserTryingToAuthenticate = (credsRequest)=>{/*todo set user to uuid if email/password match some static email pwd*/}
    let section2Auth= <Auth onAuthClick={onUserTryingToAuthenticate}/>

    let onUserTryingToLogout = ()=>{/*todo set user to null*/}

    let section2LoggedIn = <LoggedIn logoutClick={onUserTryingToLogout}/>

    return (
        <div style={{width: "max-content", padding: "8px", border: "1px solid purple"}}>
            <h1>using context apis</h1>
            <div style={{width: "max-content", padding: "8px", border: "1px solid purple"}}>
                {settings.userId!==null && section1}
                {settings.userId === null ? section2Auth : section2LoggedIn}
            </div>
        </div>
    )


}


Run Code Online (Sandbox Code Playgroud)
// rendering user login system
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode>  <UserLoginSystem/> </React.StrictMode>);

Run Code Online (Sandbox Code Playgroud)

截图:

登录 报名 登录

Dre*_*ese 5

您似乎缺少向消费者提供上下文值的MyAppSettings上下文组件。Provider

上下文本身还缺少用于MyAppSettings更新上下文所保存状态的状态更新器函数。

例子

我的应用程序设置提供者

创建一个具有默认值的上下文,显示上下文值的形状

export const MyAppSettings = React.createContext({
  userId: null,
  setUserId: () => {},
  enableMarketing: false,
  toggleConsent: () => {},
  theme: "light",
  toggleTheme: () => {},
});
Run Code Online (Sandbox Code Playgroud)

创建上下文提供者并声明状态和状态更新器函数

const MyAppSettingsProvider = ({ children }) => {
  const [userId, setUserId] = useState(null);
  const [enableMarketing, setEnableMarketing] = useState(false);
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => setTheme(theme => theme === 'light' ? 'dark' : 'light');
  const toggleConsent = () => setEnableMarketing(enabled => !enabled);

  const value = {
    userId,
    setUserId,
    enableMarketing,
    toggleConsent,
    theme,
    toggleTheme,
  };

  return (
    <MyAppSettings.Provider value={value}>
      {children}
    </MyAppSettings>
  );
};

export default MyAppSettingsProvider;
Run Code Online (Sandbox Code Playgroud)

为了方便起见,通常会创建自定义挂钩

export const useMyAppSettings = () => useContext(MyAppSettings);
Run Code Online (Sandbox Code Playgroud)

用户登录系统

该组件需要包装在 中,MyAppSettingsProvider以便它及其任何后代都可以使用MyAppSettings上下文值。

// rendering user login system
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <MyAppSettingsProvider>
      <UserLoginSystem/>
    </MyAppSettingsProvider>
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

消耗组件

消费组件导入并使用useMyAppSettings并解构它们需要引用和/或更新的上下文值。

设置界面

function SettingsUI({ onConsentChange }) {
  const {
    enableMarketing,
    theme,
    toggleConsent,
    toggleTheme
  } = useMyAppSettings();

  return(
    <div>
      <button onClick={toggleTheme}>
        Change Theme to {theme === "light" ? "dark" : "light"}
      </button>
      <br/>
      <button onClick={toggleConsent}>
        {enableMarketing
          ? "withdraw consent for marketing emails"
          : "give consent for marketing emails"
        }
      </button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

授权

function Auth({ onAuthClick }){
  const { theme } = useMyAppSettings();

  const color = theme === "light" ? "black" : "white";
  const backgroundColor = theme === "light" ? "white": "brown";

  const finalStyling = {
    border: "1px solid black",
    width: "200px",
    display: "block",
    marginBottom: "4px",
    backgroundColor,
    color,
  };

  ...

  return(
    ...
  );
}
Run Code Online (Sandbox Code Playgroud)

登录

function LoggedIn({ logoutClick }){
  const { theme } = useMyAppSettings();

  const color = theme === "light" ? "black" : "white";
  const backgroundColor = theme === "light" ? "white": "brown";

  return (
    <div style={{ padding: "8px", backgroundColor, color }}>
      <p>You have successfully logged in. your user id is {settings.userId}</p>
      <button onClick={logoutClick}>logout</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)