yarn dev运行后端代码,再回到前端代码,yarn start运行就能获取到数据了,
因为在package.json中proxy设置了端口3001

Handling Login

App.js添加登录表单
image.png

  1. const App = () => {
  2. -- snip --
  3. const [username, setUsername] = useState('')
  4. const [password, setPassword] = useState('')
  5. useEffect(() => {
  6. noteService
  7. .getAll().then(initialNotes => {
  8. setNotes(initialNotes)
  9. })
  10. }, [])
  11. // ...
  12. const handleLogin = (event) => {
  13. event.preventDefault()
  14. console.log('logging in with', username, password)
  15. }
  16. return (
  17. <div>
  18. <h1>Notes</h1>
  19. <Notification message={errorMessage} />
  20. <form onSubmit={handleLogin}>
  21. <div>
  22. username
  23. <input
  24. type="text"
  25. value={username}
  26. name="Username"
  27. onChange={({ target }) => setUsername(target.value)}
  28. />
  29. </div>
  30. <div>
  31. password
  32. <input
  33. type="password"
  34. value={password}
  35. name="Password"
  36. onChange={({ target }) => setPassword(target.value)}
  37. />
  38. </div>
  39. <button type="submit">login</button>
  40. </form>
  41. // ...
  42. </div>
  43. )
  44. }
  45. export default App

注意input元素中事件的写法

  1. <input
  2. type="text"
  3. value={username}
  4. name="Username"
  5. onChange={({ target }) => setUsername(target.value)}
  6. />

target是input元素本身,加{}转化为对象
以上写法等价于

  1. <input
  2. type="text"
  3. value={username}
  4. name="Username"
  5. onChange={( event ) => setUsername(event.target.value)}
  6. />

添加services/login.js模块

  1. import axios from 'axios'
  2. const baseUrl = '/api/login'
  3. const login = async credentials => {
  4. const response = await axios.post(baseUrl, credentials)
  5. return response.data
  6. }
  7. export default { login }

实现登录功能

  1. import loginService from './services/login'
  2. const App = () => {
  3. // ...
  4. const [user, setUser] = useState(null)
  5. // ...
  6. const handleLogin = async (event) => {
  7. event.preventDefault()
  8. try {
  9. const user = await loginService.login({
  10. username, password
  11. })
  12. setUser(user)
  13. setUsername('')
  14. setPassword('')
  15. } catch (exception) {
  16. setErrorMessage('Wrong credentials')
  17. setTimeout(() => {
  18. setErrorMessage(null)
  19. }, 5000)
  20. }
  21. }
  22. // ...
  23. }

按条件渲染表单

当用户未登录时才显示登录表单
在App组件中添加2个辅助函数, 返回值是JSX

  1. const App = () => {
  2. // ...
  3. const loginForm = () => (
  4. <form onSubmit={handleLogin}>
  5. // ...
  6. </form>
  7. )
  8. const noteForm = () => (
  9. <form onSubmit={addNote}>
  10. // ...
  11. </form>
  12. )
  13. return (
  14. // ...
  15. )
  16. }

按条件渲染

  1. const App = () => {
  2. // ...
  3. const loginForm = () => (
  4. // ...
  5. )
  6. const noteForm = () => (
  7. // ...
  8. )
  9. return (
  10. <div>
  11. // ...
  12. {user === null && loginForm()}
  13. {user !== null && noteForm()}
  14. // ...
  15. </div>
  16. )
  17. }

还可以使用三元运算符

  1. {user === null ?
  2. loginForm() :
  3. <div>
  4. <p>{user.name} logged-in</p>
  5. {noteForm}
  6. </div>
  7. }

Creating new notes

修改services/notes.js, create时将token传入

  1. import axios from 'axios'
  2. const baseUrl = '/api/notes'
  3. let token = null
  4. const setToken = newToken => {
  5. token = `bearer ${newToken}`
  6. }
  7. const getAll = () => {
  8. const request = axios.get(baseUrl)
  9. return request.then(response => response.data)
  10. }
  11. const create = async newObject => {
  12. const config = {
  13. headers: { Authorization: token },
  14. }
  15. const response = await axios.post(baseUrl, newObject, config)
  16. return response.data
  17. }
  18. const update = (id, newObject) => {
  19. const request = axios.put(`${ baseUrl } /${id}`, newObject)
  20. return request.then(response => response.data)
  21. }
  22. export default { getAll, create, update, setToken }

登录时设置token的值

  1. const handleLogin = async (event) => {
  2. event.preventDefault()
  3. try {
  4. const user = await loginService.login({
  5. username, password,
  6. })
  7. noteService.setToken(user.token)
  8. setUser(user)
  9. setUsername('')
  10. setPassword('')
  11. } catch (exception) {
  12. // ...
  13. }
  14. }

Saving the token to browsers local storage

当页面重新渲染时user信息就没了,可以将用户信息存入localStorage

  1. window.localStorage.setItem('name', 'juha tauriainen') // 存入
  2. window.localStorage.getItem('name') // 读取
  3. window.localStorage.removeItem('name') // 删除
  4. window.localStorage.clear() // 清空

存储到本地存储的值称为DOMstrings,所以我们不能存储一个 Javascript 对象。
修改登录方法:

  1. const handleLogin = async (event) => {
  2. event.preventDefault()
  3. try {
  4. const user = await loginService.login({
  5. username, password,
  6. })
  7. window.localStorage.setItem(
  8. 'loggedNoteappUser', JSON.stringify(user)
  9. )
  10. noteService.setToken(user.token)
  11. setUser(user)
  12. setUsername('')
  13. setPassword('')
  14. } catch (exception) {
  15. // ...
  16. }
  17. }

在控制台查看localStorage
image.png
使用Effect hook, 加载页面时读取user信息

  1. const App = () => {
  2. // ...
  3. useEffect(() => {
  4. const loggedUserJSON = window.localStorage.getItem('loggedNoteappUser')
  5. if (loggedUserJSON) {
  6. const user = JSON.parse(loggedUserJSON)
  7. setUser(user)
  8. noteService.setToken(user.token)
  9. }
  10. }, [])
  11. // ...
  12. }

Exercise 5.1 - 5.4

克隆代码,安装依赖

  1. git clone https://github.com/fullstack-hy/bloglist-frontend
  2. npm install
  3. npm start

get传递token

  1. // ...
  2. let token = null
  3. const setToken = newToken => {
  4. token = `bearer ${newToken}`
  5. }
  6. const getAll = async () => {
  7. const config = {
  8. headers: { Authorization: token }
  9. }
  10. const response = await axios.get(baseUrl, config)
  11. return response.data
  12. }
  13. // ...

编写事件处理函数时,别忘了event.preventDefault()
如果要获取get或post之后的数据,别忘了要用async/await, 否则得到的是undefined

  1. const createBlog = async (event) => {
  2. event.preventDefault()
  3. try {
  4. const newBlog = await blogService.create(blog)
  5. setBlogs(blogs.concat(newBlog))
  6. } catch (error) {
  7. console.log(error)
  8. }
  9. setBlog({
  10. 'title': '',
  11. 'author': '',
  12. 'url': ''
  13. })
  14. }