在这个构建中,我将展示如何使用React Context API和useReduce钩子来管理反应应用程序中的状态。为了证明这一点,我们将建立一个用户认证的反应组件,利用简单的FaceIO面部认证系统来认证我们的用户。一旦他们被认证,我们将把用户数据传递给状态,并能够在其他组件中使用它。
为什么我们要使用FaceIO
FaceIO 是一个用于网站和基于网络的应用程序的面部认证系统。
FaceIO是:
-
非常容易实现。它只需要不到3行代码,基本上需要添加一个像google gtag的脚本。
-
无缝,不需要用户获得FIDO密钥、OTP代码和安全问题,从而提高了我们网站的用户体验。
-
非常安全。首先,它提供了一个防御级别的面部识别精度,确保我们使用其面部识别引擎准确地验证每一个用户。其次,它保证了用户的隐私,确保他们的数据是安全的,而且他们实际上并不存储用户的脸,而是一个面部矢量。
我们将从上次离开的地方继续前进。首先,我们要适当地安排我们的内容。
在src文件夹中,让我们创建一个pages文件夹,并在其中创建两个文件,名为Home.tsx和SignUp.tsx。
接下来我们要添加react router dom
这样,你的构建现在看起来应该是这样的。
添加页面
我们将把App.tsx的所有内容转移到主页,除了SignUpForm。
const Home = () => {
return (
<div className='min-h-screen flex flex-col '>
<h1 className='text-3xl font-bold text-yellow-600 flex justify-center items-center'>
FaceIO authentication using react and typescript
</h1>
</div>
);
};
export default Home
把注册组件导入到注册页面。
import SignupForm from '../components/SignupForm';
const SignUp = () => {
return (
<div>
<SignupForm />
</div>
);
};
export default SignUp;
现在让我们把react route dom带入App.tsx中,这样我们就能够路由到我们应用程序的不同页面。
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import SignUp from './pages/SignUp';
function App() {
return (
<div className='min-h-screen flex flex-col justify-center items-center'>
<BrowserRouter>
<Routes>
<Route path='/' element={<Home />} />
<Route path='signup' element={<SignUp />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
由于现在我们已经完成了网站的安排,让我们进入我们构建的要点。
我们将创建一个登录函数,通过FaceIO登录(记得我们在前面的部分创建了一个注册函数)。
然后,我们将从FaceIO响应中获取用户数据,并使用useContext和useReducer将其传递到状态中。
在React和Typescript中设置useContext
让我们在src文件夹中创建一个名为context的新文件夹,在那里有一个名为StateProvider.tsx的文件,在那里我们将建立userContext提供者。
import React, {createContext, ReactNode} from 'react';
export const userContext = createContext()
const StateProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
return(
<userContext.Provider value={}>
{children}
</userContext.Provider>
)
};
export default StateProvider;
因为我们使用的是typescript,我们需要为我们的构建提供类型。
在src/@types/user.d.ts,我们将创建userContext类型。
所以我们的createContext将是userContextType类型,因为当没有用户时,用户将是空的。
现在让我们在src中创建一个helpers文件夹,并在其中创建一个名为Reducers.ts的文件。
/** src/helpers/Reducer.ts */
import { userContextType, Action } from '../@types/user';
//the initial state of the user
export const initialState = {
user: null,
};
//the action we are going to take when we login that is set the user
export const actionTypes = {
SET_USER: 'SET_USER',
};
//the reducer function note the parameter type annotations
export const reducer = (state: userContextType, action: Action) => {
console.log(action);
switch (action.type) {
case actionTypes.SET_USER:
return {
...state,
user: action.user,
};
default:
return state;
}
};
有一种类型我们没有定义,那就是动作类型,让我们相应地更新它
转到src/@types/user.d.ts,然后添加。
type Action = {
type: 'SET_USER';
user: Iuser;
};
现在一旦我们完成了这个,我们就可以像这样更新StateProvider。
import React, { createContext, ReactNode, useReducer } from 'react';
import { userContextType } from '../@types/user';
import { initialState, reducer } from '../helpers/Reducers';
export const userContext = createContext<{
state: userContextType;
dispatch: React.Dispatch<any>;
}>({
state: initialState,
dispatch: () => null,
});
const StateProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
//bring the useReducer hook and feed it with the reducer function and the initial state
const [state, dispatch] = useReducer(reducer, initialState);
return (
//update the value with the state and dispatch thus you are able to access this values in any component or page
<userContext.Provider value={{ state, dispatch }}>
{children}
</userContext.Provider>
);
};
到App.tsx中导入StateProvider,并用它来包装整个构建。
<StateProvider>
<div className='min-h-screen flex flex-col justify-center items-center'>
<BrowserRouter>
<Routes>
<Route path='/' element={<Home />} />
<Route path='signup' element={<SignUp />} />
</Routes>
</BrowserRouter>
</div>
</StateProvider>
现在让我们创建一个登录组件,以便能够将用户数据输入到状态中。
在componets文件夹中创建一个Login.tsx文件,并在其中创建一个登录。
import { Link } from 'react-router-dom';
const Login = () => {
return (
<div>
<h1 className='text-3xl font-bold text-blue-600 mb-4'>
Welcome, please login
</h1>
<button className='w-full p-3 bg-blue-700 rounded-md text-white text-sm mb-4'>
Login
</button>
<div>
<p>You don't have an account? Please sign-up.</p>
<Link to='/signup'>
<button className='w-full p-3 bg-white outline-blue-800 rounded-md text-blue-800 text-sm mb-4'>
Sign Up
</button>
</Link>
</div>
</div>
);
};
export default Login;
这个组件将是我们的门卫,它将拒绝任何人进入我们的网站,除非他们已经登录了。
所以在我们的主页组件中,我们将需要做一些改变。
我们将查询是否有一个用户的状态,即如果用户不是空的,我们将显示主页,否则我们将显示
登录组件
..... return (
<div className='min-h-screen flex flex-col '>
{!state?.user ? (
<Login />
) : (
<div>
<h1 className='text-3xl font-bold text-yellow-600 flex justify-center items-center'>
FaceIO authentication using react and typescript
</h1>
</div>
)}
</div>
);
这就是你的页面现在应该是的样子。
登录组件
现在让我们回到登录组件并更新逻辑,即调用FaceIO并分配用户数据。
当我们调用FaceIO进行认证时(FaceIO认证在这里有记录),认证成功后,我们将收到一个包含用户注册时提供的数据的有效载荷。这个有效载荷就是我们要分配给状态的东西。
import React, { useContext, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { userContext } from '../context/StateProvider';
import { actionTypes } from '../helpers/Reducers';
const Login = () => {
//we load faceIO using a useEffect hook
let faceio: any;
useEffect(() => {
faceio = new faceIO('fioa48b7');
}, []);
//we use a useContext hook dispatch to be able to dispatch our user to the state
const { dispatch } = useContext(userContext);
//we set up the handle login function
const handleLogin = async () => {
try {
let response = await faceio.authenticate({
locale: 'auto',
});
alert(`
You have been identified successfully
Unique Facial ID: ${response.facialId}
PayLoad: ${JSON.stringify(response.payload)}
`);
dispatch({ type: actionTypes.SET_USER, user: response.payload });
alert('You have successfully logged in');
} catch (error) {
console.log(error);
alert('Failed to login, please refresh and try again');
}
};
return (
<div className='flex flex-col justify-center items-center'>
<h1 className='text-3xl font-bold text-blue-600 mb-4'>
Welcome, please login
</h1>
<button onClick={handleLogin} className='w-full p-3 bg-blue-700 rounded-md text-white text-sm mb-4'>
Login
</button>
<div>
<p>You don't have an account? Please sign-up.</p>
<Link to='/signup'>
<button
className='w-full p-3 bg-white outline-blue-800 rounded-md text-blue-800 text-sm mb-4'
>
Sign Up
</button>
</Link>
</div>
</div>
);
};
export default Login;
一旦我们完成了这些工作,我们的应用程序就做好了准备,但让我们在主页上添加一个欢迎用户的声明。
在h1标签下添加。
<h2 className='text-blue-900 pt-28 font-bold'>
Welcome {state.user.name} Email:{state.user.email}
</h2>
我们现在完成了,如果你成功了,你尝试登录,你会看到。
构建完成
这个构建的全部内容可以在Github上找到,当你在那里的时候,请给它加一颗星。