/src/exercises/16.js

https://github.com/kentcdodds/learn-react · JavaScript · 134 lines · 77 code · 11 blank · 46 comment · 1 complexity · 8b757f1f97eb9ede714fb4d961d5b00d MD5 · raw file

  1. // Making HTTP requests
  2. import React from 'react'
  3. // 🦉 There are lots of ways to make HTTP requests in React components. React is
  4. // currently working on a new feature called Suspense which is fantastic and
  5. // will solve lots of problems web applications (not just React) have with
  6. // data fetching on the client. Unfortunately, it's not ready yet.
  7. //
  8. // 🦉 Every company has unique data requirements, so I recommend that you either
  9. // find an open source custom hook that suits your needs or you develop one
  10. // for your company that suits your needs well.
  11. //
  12. // In this exercise, we'll be doing data fetching directly in a useEffect hook
  13. // callback within our component.
  14. //
  15. // Here we have a form where users can enter the name of a pokemon and fetch
  16. // data about that pokemon. Your job will be to create a component which makes
  17. // that fetch request.
  18. // eslint-disable-next-line no-unused-vars
  19. function fetchPokemonReducer(state, action) {
  20. switch (action.type) {
  21. case 'FETCHING': {
  22. // 🐨 return the state that should exist when fetching starts
  23. return state
  24. }
  25. case 'FETCHED': {
  26. // 🐨 return the state that should exist when the fetch request finishes
  27. return state
  28. }
  29. case 'FETCH_ERROR': {
  30. // 🐨 return the state that should exist when the fetch request fails
  31. return state
  32. }
  33. default:
  34. throw new Error(`Unhandled action type: ${action.type}`)
  35. }
  36. }
  37. function FetchPokemon({pokemonName}) {
  38. // 🐨 Have state for the pokemon (null), the error state (null), and the
  39. // loading state (false). I recommend you use a reducer for this. I've given
  40. // you a starter reducer above because I love you.
  41. // 🐨 Use the `fetchPokemon` function below to fetch a pokemon by its name:
  42. // fetchPokemon('Pikachu').then(
  43. // pokemon => { /* call set state with the pokemon and loading: false */},
  44. // error => {/* call set state with the error loading: false */},
  45. // )
  46. // 🐨 use React.useEffect where the callback should be called whenever the
  47. // pokemon name changes.
  48. // 💰 DON'T FORGET THE DEPENDENCIES ARRAY!
  49. // 🐨 before calling `fetchPokemon`, make sure to dispatch a FETCHING action
  50. // 🐨 when the promise resolves, dispatch FETCHED and send the pokemon
  51. // 🐨 if the promise rejects, dispatch a FETCH_ERROR and send the error
  52. // 🐨 Render the appropriate content based on the state:
  53. // 1. loading: '...'
  54. // 2. error: 'ERROR!'
  55. // 3. pokemon: the JSON.stringified pokemon in a <pre></pre>
  56. return 'todo'
  57. }
  58. ////////////////////////////////////////////////////////////////////
  59. // //
  60. // Don't make changes below here. //
  61. // But do look at it to see how your code is intended to be used. //
  62. // //
  63. ////////////////////////////////////////////////////////////////////
  64. function fetchPokemon(name) {
  65. const pokemonQuery = `
  66. query ($name: String) {
  67. pokemon(name: $name) {
  68. id
  69. number
  70. name
  71. attacks {
  72. special {
  73. name
  74. type
  75. damage
  76. }
  77. }
  78. }
  79. }
  80. `
  81. return window
  82. .fetch('https://graphql-pokemon.now.sh', {
  83. // learn more about this API here: https://graphql-pokemon.now.sh/
  84. method: 'POST',
  85. headers: {
  86. 'content-type': 'application/json;charset=UTF-8',
  87. },
  88. body: JSON.stringify({
  89. query: pokemonQuery,
  90. variables: {name},
  91. }),
  92. })
  93. .then(r => r.json())
  94. .then(response => response.data.pokemon)
  95. }
  96. class Usage extends React.Component {
  97. state = {pokemonName: null}
  98. inputRef = React.createRef()
  99. handleSubmit = e => {
  100. e.preventDefault()
  101. this.setState({
  102. pokemonName: this.inputRef.current.value,
  103. })
  104. }
  105. render() {
  106. const {pokemonName} = this.state
  107. return (
  108. <div>
  109. <form onSubmit={this.handleSubmit}>
  110. <label htmlFor="pokemonName-input">Pokemon Name (ie Pikachu)</label>
  111. <input id="pokemonName-input" ref={this.inputRef} />
  112. <button type="submit">Submit</button>
  113. </form>
  114. <div data-testid="pokemon-display">
  115. {pokemonName ? <FetchPokemon pokemonName={pokemonName} /> : null}
  116. </div>
  117. </div>
  118. )
  119. }
  120. }
  121. Usage.title = 'Making HTTP requests'
  122. export default Usage
  123. /* eslint no-unused-vars:0 */