使用 React 和 Redux 构建公司主题简历生成器
本指南将帮助您使用 Reactjs 构建一个公司主题的简历生成器。您只需填写个人信息,然后选择您想要申请的公司。该 Web 应用程序将根据您申请的公司自动生成简历。
步骤 1 - 设置 Redux store
首先,我们需要创建一个 store 来存储应用程序的状态树。我在 index.js 文件中创建了这个 store。要将状态的任何更改应用到 Redux 中,我们需要分发 action 并编写其 reducer 函数。
const store = createStore(rootReducer);
然后我们将 App 组件包裹在 Provider 组件中。这样,所有嵌套组件都可以访问 Redux store。
<Provider store={store}>
<App />
</Provider>
现在,我们在所有其他组件内部都使用了一个连接函数。这样,每当状态更新时,该组件就可以重新读取状态值。
const mapStateToProps = (state) => {
return{
state
}
};
const TwitterContainer = connect(mapStateToProps, null)(Twitter);
export default TwitterContainer;
`Connect()` 函数接受两个参数,这两个参数都是可选的。我只使用了第一个参数:`mapStateToProps`。它会在每次 store 状态改变时被调用。它接收整个 store 状态,并返回该组件所需的数据对象。
步骤 2 - 从用户处获取详细信息
创建输入字段是为了获取用户输入。这些输入包括姓名、联系电话、电子邮件地址、工作经历、教育背景、项目、技能以及 LinkedIn 和作品集的链接。状态包含这些信息作为属性,初始状态为空。工作经历、教育背景、项目和技能均使用数组存储。数组中的每个元素包含:
1) ID;
2) 子标题;
3) 描述。
work_experience: [{
id: 1,
subheading: '',
desc: ''
}
]
在这些字段中输入内容后,将会触发一个操作。
const addName = (value) => {
props.dispatch({
type: "ADD_NAME",
text: value
});
}
reducer会检查 action 的类型并相应地更改状态。要获取工作经验、教育背景、项目和技能等字段的输入,还需要 id。
以下代码用于工作示例的子标题。
const addWorkSubheading = (i,value) =>{
props.dispatch({
type: 'ADD_WORK_SUBHEADING',
id: i,
text: value
});
}
类似地,也会针对其他子标题和描述执行相应的操作。
为了根据用户输入更改状态,首先将 action.id 与 work-ex 数组中所有元素的 id 进行比较,并将要更改的子部分赋值给一个变量,将其索引赋值给另一个变量。
现在,如果数组长度为 1,则使用扩展运算符展开状态,然后在 work-ex 数组中展开第一个元素并赋值。
如果长度为 2,则展开状态并使用 switch 语句检查子部分的索引。如果索引为 0,则更改第一个元素并返回第二个元素,反之亦然。
如果长度大于 2,则展开状态并从 0 到 subsectionIndex 切片 work-ex 数组并返回,展开所需的子部分并进行更改,然后再次从 (subsectionIndex + 1) 切片到末尾并返回。
case 'ADD_WORK_SUBHEADING':
const subsection = state.work_experience.filter(w=>{ return w.id === action.id })[0];
const subsectionIndex = state.work_experience.findIndex(w=>{ return w.id === action.id });
if (state.work_experience.length <= 1){
return{
...state,
work_experience: [
{
...state.work_experience[0],
subheading: action.text
}
]
};
}
else if (state.work_experience.length === 2) {
switch (subsectionIndex) {
case 0:
return {
...state,
work_experience: [
{
...state.work_experience[0],
subheading: action.text
},
state.work_experience[1]
]
};
case 1:
return {
...state,
work_experience: [
state.work_experience[0],
{
...state.work_experience[1],
subheading: action.text
}
]
};
}
}
else {
return {
...state,
work_experience: [
...state.work_experience.slice(0, subsectionIndex),
{
...subsection,
subheading: action.text
},
...state.work_experience.slice(subsectionIndex+1, state.work_experience.length)
]
};
}
同样,其他子标题和描述的状态也会发生变化。
现在要添加子部分,这里有一个加号按钮。点击此按钮会触发一个 action。在 reducer 中,首先将 state 展开。然后展开 work-ex,并向数组中添加一个元素,该元素的 id 为数组长度加 1。
case 'ADD_WORK_SUBSECTION':
return {
...state,
work_experience: [
...state.work_experience,
{
id: state.work_experience.length+1,
subheading: '',
desc: ''
}
]
};
步骤三:预览简历
填写完详细信息后,您可以预览简历以用于不同公司。
点击这些按钮,即可获得您的专属主题简历。
用户输入的数据将根据您在此页面选择的公司以自定义样式显示。
步骤 4 - 使用 GitHub 登录
为了构建登录功能,我使用了 react-firebase auth。
使用 react-firebase 构建身份验证时,需要遵循以下步骤:
步骤 1
在 Firebase 中创建一个项目,并启用您希望在项目中使用的注册方式。
步骤 2
安装 React Firebase。npm
i @react-firebase/auth
步骤 3
使用以下链接获取您的 Firebase 配置:
https://console.firebase.google.com/project/PROJECT_NAME/settings/general/
将 PROJECT_NAME 替换为您的 Firebase 项目名称。
将此配置粘贴到名为 config.js 的文件中,然后导出配置。
第四步
在项目中导入 Firebase。
import firebase from "firebase/app";
import "firebase/auth";
import {
FirebaseAuthProvider,
FirebaseAuthConsumer
} from "@react-firebase/auth";
import { config } from "./config";
第五步
将您的应用代码封装在 FirebaseAuthProvider 和 FirebaseAuthConsumer 中:
<FirebaseAuthProvider firebase={firebase} {...config}>
<div>
<FirebaseAuthConsumer>
{({ isSignedIn, user, providerId}) => {
if(isSignedIn === true){
return(
<div>
<Router>
<Switch>
<Route exact path="/" render={() => <MainApp uid={user.uid}/>} />
</div>
);
}
else{
return(
<div className="signin-div">
<button
className="signin"
onClick={() => {
const githubAuthProvider = new firebase.auth.GithubAuthProvider();
firebase.auth().signInWithPopup(githubAuthProvider);
}}>
Sign In with Github
</button>
</div>
);
}
}}
</FirebaseAuthConsumer>
</div>
</FirebaseAuthProvider>
如果用户已登录,FirebaseAuthConsumer 返回isSignedIn 为 true;如果没有用户登录,则返回false
。 根据此条件,要么渲染 MainApp 和所有其他组件,要么渲染一个包含注册按钮的页面。
步骤 5 - 将用户数据存储在 Firebase Cloud Firestore 中
以下步骤用于在 Cloud Firestore 中创建和存储数据。
步骤 1
进入你的项目并导航至 Cloud Firestore。选择启动模式为“测试模式”。选择位置并点击“完成”。
步骤 2
安装 Cloud Firestore SDK
:npm install firebase@8.4.3 --save
步骤 3
在你的项目中创建一个名为 database.js 的文件,并导入 firestore。
import firebase from "firebase/app";
import "firebase/firestore";
第四步
初始化 Cloud Firestore 和数据库,并导出数据库。
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});
const db = firebase.firestore();
export default db;
将数据库导入到需要保存数据或获取数据的文件中。
第五步
现在,要将数据保存到 Firestore 中,需要使用保存按钮。该按钮位于用户详情页面。
点击此按钮后,将运行以下代码。
const saveData = () => {
db.collection("users").doc(props.uid).set({
name: props.state.name,
contact: props.state.contact,
email: props.state.email,
work_experience: props.state.work_experience,
education: props.state.education,
projects: props.state.projects,
skills: props.state.skills,
linkedin: props.state.linkedin,
portfolio: props.state.portfolio
})
.then(() => {
console.log("Document successfully written!");
})
.catch((error) => {
console.error("Error writing document: ", error);
});
}
运行这段代码后,数据库中会创建一个名为“users”的集合。身份验证时,我们会获取用户ID。数据库中会为不同的UID创建不同的文档。状态数据会使用`.set()`方法保存到数据库中。
步骤 6 - 从 Firebase Cloud Firestore 中检索用户数据
当主页挂载时,将从 Cloud Firestore 检索数据。
const fetchUsers = async () => {
await db.collection("users").doc(props.uid).get().then((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data().portfolio);
props.dispatch({
type: "ADD_DATA",
text: doc.data()
})
}
else {
console.log("No such document!");
}
}).catch((error) => {
console.log("Error getting document:", error);
});
};
useEffect( () => { fetchUsers() }, [] );
使用useEffect 并传入一个空数组,我们不会监听任何变量。因此,它只会在组件首次渲染时(例如 componentDidMount())更新状态。
在 fetchUsers 函数内部,会调用 .get() 方法,并将 "users" 作为集合, "uid" 作为文档。它会检索该 uid 对应的数据。然后会分发一个 action,并在 reducer 函数中对状态进行如下更改。
case 'ADD_DATA':
return{
...state,
name: action.text.name,
contact: action.text.contact,
email: action.text.email,
work_experience: action.text.work_experience,
education: action.text.education,
projects: action.text.projects,
skills: action.text.skills,
linkedin: action.text.linkedin,
portfolio: action.text.portfolio,
firebaseprocessing: false
};
步骤 7 - 分享简历链接
选择您想要创建简历的公司后,您将进入一个页面,页面上会显示您的简历和分享按钮。点击此按钮后,您将获得一个链接。请复制此链接并粘贴到您想要的位置。
要获取此链接,首先我们需要主机名、协议和端口。
const hostname = window.location.hostname;
const protocol = window.location.protocol;
const port = window.location.port;
为了显示这个链接,需要创建一个包含该链接的 div 元素,该元素仅在点击分享按钮时可见,点击除该 div 元素之外的任何其他位置时都会消失。为此,我使用了 ClickAwayListener 。您可以在https://www.npmjs.com/package/react-click-away-listener上了解更多信息。
{(props.state.link === true)?
<ClickAwayListener onClickAway={e=>hideLink()}>
<section className="link-part3" >
{(port === 0 || port === '')?
<p>Copy this link {protocol}//{hostname}/{props.uid}/amazon</p>:
<p>Copy this link {protocol}//{hostname}:{port}/{props.uid}/amazon</p>
}
</section>
</ClickAwayListener>:
<Fragment />
}
在本代码段中,首先检查 `props.state.link` 是否为真。它用于显示链接。然后,检查端口号是否为 0、空字符串或其他值。
如果端口号为 0 或空字符串,则表示默认情况(HTTP 使用 80 端口,HTTPS 使用 443 端口)。在默认情况下,链接中不需要指定端口号。
如果端口号不为 0 或空字符串,则需要在链接中指定端口号。
链接还会包含一个 uid,用于在用户点击链接时检索数据。
步骤 8 - 通过链接查看简历
为了表明该链接是从外部输入的,在路由该链接时,会将参数external传递给 props。
<Route path="/:id/youtube" render={() => <Youtube external={true} />}/>
在 YouTube 组件内部,使用 useEffect 时,我们会检查 props.external 是否为 true。这用于判断链接是否来自外部。
如果 props.external 为 true,则会调用 fetchUsers 并传入用户 ID。我们使用链接中的 ID 来实现这一点,为此使用了 useParams()。
如果 props.external 为 false,则表示这是一个内部链接,此时会调用 fetchUsers 并传入 props.uid。
这用于获取与当前用户 ID 对应的文档,该用户正在查看其简历。
const fetchUsers = async (i) => {
await db.collection("users").doc(i).get().then((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data().portfolio);
props.dispatch({
type: "ADD_DATA",
text: doc.data()
});
}
else {
console.log("No such document!");
}
}).catch((error) => {
console.log("Error getting document:", error);
});
};
useEffect( () => {
(props.external)?
fetchUsers(id):
fetchUsers(props.uid)
}, [] );
现在,系统会从 Cloud Firestore 中检索用户数据,并据此更改状态。当用户点击任何链接时,系统会使用存储中的数据来显示恢复过程中的信息。
源代码:https://github.com/shambhavijs/themed-resume
在线演示:https://bit.ly/2SiZUZ4