Context
Context
2020/11/17
利用persistent state(例如:SecureStore) 可以在手機裡儲存資料,不過,從persistent state裡讀入資料之後,是不是可以將登入狀態跟所有的元件分享? 在react native裡可以利用context來達成這樣的效果,或者,使用更複雜的Redux來達成這樣的效果,我們先來試試看Context。
首先,我們可以先利用state變數(isSignedIn)來決定在tab navigator可顯示內容
function App() {
const [count, setCount] = useState(10);
const [isSignedIn, setIsSignedIn] = useState(false);
當isSignedIn是false的時候,就會看到Sign In及Sign Up。
<NavigationContainer>
<Tab.Navigator>
{isSignedIn?(
<>
<Tab.Screen name="Person" component={PersonList} />
<Tab.Screen name="Product" component={ProductList} />
</>
)
:(
<>
<Tab.Screen name="Sign In" component={SignIn} />
<Tab.Screen name="Sign Up" component={SignUp} />
</>
)
}
</Tab.Navigator>
</NavigationContainer>
那如果要讓PersonList收到isSignedIn,通常就可以使用props,因為我們使用Navigator,所以,要透過initialParams。只是,這樣子要一層一層的傳遞變數,有點麻煩。在react裡提供了Context,利用Context可以創造類似全域變數的效果。首先,先利用createContext,產生一個context物件。
createContext
/src/account/AuthContext.js
import React from 'react';
export const AuthContext = React.createContext({
isSignedIn: false
})
context.Provider
在/App.js裡先import AuthContext
import {AuthContext} from './src/account/AuthContext';
接下來,將需要使用AuthContext利用AuthContext.Provider包起來,並把App裡isSignedIn的值透過傳給AuthContext裡的isSignedIn:
<NavigationContainer>
<AuthContext.Provider value={{isSignedIn: isSignedIn}}>
<Tab.Navigator>
{isSignedIn?(
<>
<Tab.Screen name="Person" component={PersonList} />
<Tab.Screen name="Product" component={ProductList} />
</>
)
:(
<>
<Tab.Screen name="Sign In" component={SignIn} />
<Tab.Screen name="Sign Up" component={SignUp} />
</>
)
}
</Tab.Navigator>
</AuthContext.Provider>
</NavigationContainer>
useContext
在PersonList裡,先import useContext及AuthContext
import React, {useState, useEffect, useContext} from 'react';
import {AuthContext} from '../account/AuthContext';
在PersonList裡,利用useContext hook取得AuthContext裡的值。
export default function PersonList() {
const get_url=url+"?maxRecords=50&view=Grid%20view";
const [persons, setPersons] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [selectedId, setSelectedId] = useState(null);
const [person, setPerson] = useState({
Name:"",
City:"",
Age:0}
);//temp variable for edit
const [isLoading, setIsLoading] = useState(true);
const authContext = useContext(AuthContext);
在useEffect裡,可以看到取得儲存在Context裡的資料。
useEffect(() => {
console.log("isSignedIn in PersonList:"+authContext.isSignedIn);
setIsLoading(true);
console.log("fetchData");
console.log(`url:${url}`);
fetchData();
},[modalVisible]);
試試看,把App.js裡的isSignedIn預設值改為true,然後,點選Person就可以看到PersonList成功的取得isSignedIn。
function App() {
const [count, setCount] = useState(10);
const [isSignedIn, setIsSignedIn] = useState(true);
記得改回去:
function App() {
const [count, setCount] = useState(10);
const [isSignedIn, setIsSignedIn] = useState(false);
更動context
那要怎麼更動Context裡的值呢? 先在AuthContext留一個空的方法,其實這裡的變數值及方法都會在設定Provider時覆蓋。
/src/account/AuthContext.js
import React from 'react';
export const AuthContext = React.createContext({
isSignedIn: false,
setStatus: ()=>{}
})
//isSignedIn & setStatus will be replaced in App.js
在App.js裡,把App的setIsSignedIn取代AuthContext裡的setStatus。
return (
<NavigationContainer>
<AuthContext.Provider value={{isSignedIn: isSignedIn, setStatus:setIsSignedIn }}>
在SignIn.js裡
import {AuthContext} from '../account/AuthContext';
利用useContext取得authContext
export default function SignIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [message, setMessage] = useState("");
const authContext = useContext(AuthContext);
呼叫authContext裡的setStatus,記得,事實上是呼叫App的setIsSignedIn。
async function signIn(){
try {
const res= await firebase.auth()
.signInWithEmailAndPassword(email, password);
//console.log('User login successfully!');
const loginString = JSON.stringify({email:email, password:password});
await SecureStore.setItemAsync("login", loginString);
setEmail('');
setPassword('');
setMessage('');
//console.log("account saved");
authContext.setStatus(true);
}
catch(error){
setMessage(error.message);
}
};
透過 React CreateContext 搭配 React useContext 與 useReducer 來做 Global State Manager
Authentication flows (react navigation & context)
利用useContext、useMemo、useReducer
A Practical React Native Problem Solved with the Context API