React Hooks demo

react hooks demo

  1. 创建所需要的组件,这个项目我们分成三个组件, 分别是头部,搜索框,电影列表

    // 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>
        )
    }
    
  2. 创建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'
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343