npm install -g create-react-app
create-react-app <item-name>
yarn start
(1) 把 App.js 拉到该目录下,同时更新 index.js 下 App.js 的路径
(2) 创建Header.js,header 组件,接收父组件传过来的标题,App-header 是 App.css 中的一个 class
import React from "react";
const Header = (props) => {
return (
<header className="App-header">
<h2>{props.text}</h2>
</header>
);
};
export default Header;
(3) 创建 Movie.js,该组件用于展示父组件传过来的电影相关数据
import React from "react";
const DEFAULT_PLACEHOLDER_IMAGE =
"https://m.media-amazon.com/images/M/MV5BMTczNTI2ODUwOF5BMl5BanBnXkFtZTcwMTU0NTIzMw@@._V1_SX300.jpg";
const Movie = ({ movie }) => {
const poster =
movie.Poster === "N/A" ? DEFAULT_PLACEHOLDER_IMAGE : movie.Poster;
return (
<div className="movie">
<h2>{movie.Title}</h2>
<div>
<img
width="200"
alt={`The movie titled: ${movie.Title}`}
src={poster}
/>
</div>
<p>({movie.Year})</p>
</div>
);
};
export default Movie;
(4) 创建 Search.js,该组件实现搜索功能,其中用到了 react 的 useState 钩子
import React, { useState } from "react";
const Search = (props) => {
const [searchValue, setSearchValue] = useState("");
const handleSearchInputChanges = (e) => {
setSearchValue(e.target.value);
}
const resetInputField = () => {
setSearchValue("")
}
const callSearchFunction = (e) => {
e.preventDefault();
props.search(searchValue);
resetInputField();
}
return (
<form className="search">
<input
value={searchValue}
onChange={handleSearchInputChanges}
type="text"
/>
<input onClick={callSearchFunction} type="submit" value="SEARCH" />
</form>
);
}
export default Search;
import React, { useReducer, useEffect } from "react";
import "../App.css";
import Header from "./Header";
import Movie from "./Movie";
import Search from "./Search";
const MOVIE_API_URL = "https://www.omdbapi.com/?s=man&apikey=4a3b711b";
const initialState = {
loading: true,
movies: [],
errorMessage: null
};
const reducer = (state, action) => {
switch (action.type) {
case "SEARCH_MOVIES_REQUEST":
return {
...state,
loading: true,
errorMessage: null
};
case "SEARCH_MOVIES_SUCCESS":
return {
...state,
loading: false,
movies: action.payload
};
case "SEARCH_MOVIES_FAILURE":
return {
...state,
loading: false,
errorMessage: action.error
};
default:
return state;
}
};
const App = () => {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
fetch(MOVIE_API_URL)
.then(response => response.json())
.then(jsonResponse => {
dispatch({
type: "SEARCH_MOVIES_SUCCESS",
payload: jsonResponse.Search
});
});
}, []);
const search = searchValue => {
dispatch({
type: "SEARCH_MOVIES_REQUEST"
});
fetch(`https://www.omdbapi.com/?s=${searchValue}&apikey=4a3b711b`)
.then(response => response.json())
.then(jsonResponse => {
if (jsonResponse.Response === "True") {
dispatch({
type: "SEARCH_MOVIES_SUCCESS",
payload: jsonResponse.Search
});
} else {
dispatch({
type: "SEARCH_MOVIES_FAILURE",
error: jsonResponse.Error
});
}
});
};
const { movies, errorMessage, loading } = state;
return (
<div className="App">
<Header text="REACT MOVIE APP" />
<Search search={search} />
<p className="App-intro">Sharing a few of our favourite movies</p>
<div className="movies">
{loading && !errorMessage ? (
<span>loading... </span>
) : errorMessage ? (
<div className="errorMessage">{errorMessage}</div>
) : (
movies.map((movie, index) => (
<Movie key={`${index}-${movie.Title}`} movie={movie} />
))
)}
</div>
</div>
);
};
export default App;