react hooks demo
-
创建所需要的组件,这个项目我们分成三个组件, 分别是头部,搜索框,电影列表
// Header.js import React from 'react' export default function Header(props) { return ( <div> <header className="app_header"> <h2>{props.text}</h2> </header> </div> ) }
// Movie.js import React from 'react' const DEFAULT_PLACEHOLDER_IMAGE = 'https://m.media-amazon.com/images/M/MV5BMTczNTI2ODUwOF5BMl5BanBnXkFtZTcwMTU0NTIzMw@@._V1_SX300.jpg' export default function Movie({movie}) { const poster = movie.Poster = 'N/A' ? DEFAULT_PLACEHOLDER_IMAGE : movie.Poster return (f <div className="movie"> <h2>{movie.Title}</h2> <div> <img width="200" src={poster} alt={`The Movie Titled: ${movie.Title}`} /> </div> </div> ) }
// Search.js import React, { useState } from 'react' export default function Search(props) { const [searchValue, setSearchValue] = useState('') const handleSearchInputChanges = (e) => { setSearchValue(e.target.value) } const resetInputField = () => { setSearchValue('') } const handleInputSearch = (e) => { e.preventDefault() props.search(searchValue) resetInputField() } return ( <div> <form className="search"> <input type="text" onChange={handleSearchInputChanges} value={searchValue} /> <input type="submit" onClick={handleInputSearch} value="SEARCH" /> </form> </div> ) }
-
创建App.js
// App.js import React, { useState, useEffect } from 'react' import Header from './Header.js' import Movie from './Movie.js' import Search from './Search.js' import axios from 'axios' const MOVIE_API_URL = 'https://www.omdbapi.com/?s=man&apikey=62b53dxx' export default function App() { const [loading, setLoading] = useState(true) const [movie, setMovie] = useState([]) const [errorMessage, setErrorMessage] = useState(null) /** 这里我们使用了三个useState函数,所以在一个组件中可以有多个useState函数 1. loading: 用于处理加载状态,加载时页面呈现一个Loading 2. movie: 用于处理从服务器获取的movies数组/数据 3. errorMessage: 用于处理在发出API请求时可能发生的任何错误 */ const search = (searchVal) => { setLoading(false) setErrorMessage(null) axios.get(`https://www.omdbapi.com/?s=${searchVal}&apikey=62b53dxx`).then(res => { if (res.data.Response === 'True') { setMovie(res.data.Search) } else { setErrorMessage(res.data.Error) } setLoading(false) }) } useEffect(() => { axios.get(MOVIE_API_URL).then(res => { setMovie(res.data.Search) setLoading(false) }) }, []) /** 这里使用了useEffect,这个Hooks可以让你在函数式组件中执行副作用, 所谓的副作用是指数据获取,订阅和手动操作DOM 我们也可以将useEffect看作是componentDidMount, componentDidUpdate和componentWillUnmount的组合(这是因为在组件第一次render之后(componentDidMount)和每次组件更新之后(componentDidUpdate), useEffect都会被调用) */ return ( <div className="App"> <Header text={Movies Demo} /> <Search search={search} /> <div className="movies"> { loading && !errorMessage ? (<span>loading...</span>) : errorMessage ? (<div>{errorMessage}</div>) : (movie.map((item, index) => {<Movie key={`${index}-${movie.Title}`} movie={item} />})) } </div> </div> ) }
如果项目中需要定义多个状态,那我们使用
useState
会显得代码比较冗余,也比较乱,建议用useReducer
,统一管理下代码。我们修改App.js
文件如下:import React, { useReducer } from 'react' import Header from './Header' import Movie from './Movie' import Search from './Search' import axios from 'axios' const initialState = { loading: true, movie: [], errorMessage: null } const reducer = (state, action) => { switch(action.type) { case 'SEARCH_MOVIE_REQUEST': return { ...state, loading: true, errorMessage: null } case "SEARCH_MOVIE_SUCCESS": return { ...state, loading: false, movie: action.payload } case 'SEARCH_MOVIE_FAILURE': return { ...state, loading: false, errorMessage: action.error } default: return state } } export default App() { const [state, dispatch] = useReducer(reducer, initialState) const { loading, movie, errorMessage } = state const search = (searchVal) => { // setLoading(false) // setErrorMessage(null) // 修改为: dispatch({ type: 'SEARCH_MOVIE_REQUEST' }) axios.get(`https://www.omdbapi.com/?s=${searchVal}&apikey=62b53dxx`).then(res => { if (res.data.Response === 'True') { // setMovie(res.data.Search) // 修改为: dispatch({ type: 'SEARCH_MOVIE_SUCCESS', payload: res.data.Search }) } else { // setErrorMessage(res.data.Error) // 修改为: dispatch({ type: 'SEARCH_MOVIE_FAILURE', error: res.data.Error }) } // setLoading(false) }) } useEffect(() => { axios.get(MOVIE_API_URL).then(res => { // setMovie(res.data.Search) // setLoading(false) // 修改为: dispatch({ type: 'SEARCH_MOVIE_SUCCESS', payload: res.data.Search }) }) }, []) return ( <div className="App"> <Header text={Movies Demo} /> <Search search={search} /> <div className="movies"> { loading && !errorMessage ? (<span>loading...</span>) : errorMessage ? (<div>{errorMessage}</div>) : (movie.map((item, index) => {<Movie key={`${index}-${movie.Title}`} movie={item} />})) } </div> </div> ) }
这样好像看起来还是代码有点多,而且全部都写在
App.js
这个文件中, 我们可以把state和reducer拆分到另外的文件,然后再引入我们在
src
目录下新建一个store文件夹,store目录下再建一个reducer文件夹// src/store/reducer/index.js export const initialState = { loading: true, movie: [], errorMessage: null } export const reducer = (state, action) => { switch(action.type) { case: 'SEARCH_MOVIE_REQUSET': return { ...state, loading: false, errorMessage: null } case: 'SEARCH_MOVIE_SUCCESS': return { ...state, loading: true, movie: action.payload } case: 'SEARCH_MOVIE_FAILURE': return { ...state, loading: true, errorMessage: action.error } default: return state } }
在
App.js
中引入,然后就可以使用我们自定义的state和reducer了import { initialState, reducer } from '../store/reducer'