Reputation: 1179
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 component 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 components , 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 components 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 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>
)
}
//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>
)
}
// rendering user login system
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode> <UserLoginSystem/> </React.StrictMode>);
Screenshots:
Upvotes: 1
Views: 6142
Reputation: 202872
You appear to be missing the MyAppSettings
context Provider
component 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.
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);
This component 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>
);
The consuming component 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>
);
}
Upvotes: 6