最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to update react context from multiple conmponents? - Stack Overflow

programmeradmin2浏览0评论

So i am trying to lear about React contexts and I am a bit confused. from its documentation:

Context provides a way to pass data through the ponent tree without having to pass props down manually at every level.

So this means i can have the whole state of an app as global and i can update it from any child ponents , right? However I am confused about how to go with it. I have a small app that shows either login, signup or Logged in screen to user based on their input. i am expecting that any of the following ponents should be able to change the value of global object stored in context, but i am unsure about how to go with it (mentioned the unsure functions as todos )

// context
const MyAppSettings = React.createContext(
    {
        userId:null,
        enableMarketing:false,
        theme:"light"
    }
)
//ui ponents(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>
    )
}

//ponent controlling  the other ponents 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>
    )


}


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

Screenshots:

So i am trying to lear about React contexts and I am a bit confused. from its documentation:

Context provides a way to pass data through the ponent tree without having to pass props down manually at every level.

So this means i can have the whole state of an app as global and i can update it from any child ponents , right? However I am confused about how to go with it. I have a small app that shows either login, signup or Logged in screen to user based on their input. i am expecting that any of the following ponents should be able to change the value of global object stored in context, but i am unsure about how to go with it (mentioned the unsure functions as todos )

// context
const MyAppSettings = React.createContext(
    {
        userId:null,
        enableMarketing:false,
        theme:"light"
    }
)
//ui ponents(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>
    )
}

//ponent controlling  the other ponents 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>
    )


}


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

Screenshots:

Share Improve this question edited Jul 25, 2022 at 3:36 Drew Reese 204k18 gold badges245 silver badges273 bronze badges asked Jul 24, 2022 at 19:08 ansh sachdevaansh sachdeva 1,1811 gold badge18 silver badges36 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

You appear to be missing the MyAppSettings context Provider ponent that provides the context value to consumers.

Also missing from the MyAppSettings Context itself are the state updater functions to update the state the context holds.

Example

MyAppSettingsProvider

Create a context with default value showing the shape of the Context value

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

Create the context Provider and declare the state and state updater functions

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;

Oftentimes a custom hook will be created for convenience

export const useMyAppSettings = () => useContext(MyAppSettings);

UserLoginSystem

This ponent needs to be wrapped in the MyAppSettingsProvider so it and any of its descendants can consume the MyAppSettings context value.

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

Consuming ponents

The consuming ponent import and use the useMyAppSettings and destructure the context values they need to reference and/or update.

SettingsUI

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>
  );
}

Auth

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(
    ...
  );
}

Login

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>
  );
}
发布评论

评论列表(0)

  1. 暂无评论