eslint no-nested-ternary の対応

やりたい事

How to build a movie search app using React Hooksからコードを引用

import React, { useState, 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'; // you should replace this with yours


const App = () => {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const [errorMessage, setErrorMessage] = useState(null);

  useEffect(() => {
    fetch(MOVIE_API_URL)
      .then((response) => response.json())
      .then((jsonResponse) => {
        setMovies(jsonResponse.Search);
        setLoading(false);
      });
  }, []);

  const search = (searchValue) => {
    setLoading(true);
    setErrorMessage(null);

    fetch(`https://www.omdbapi.com/?s=${searchValue}&apikey=4a3b711b`)
      .then((response) => response.json())
      .then((jsonResponse) => {
        if (jsonResponse.Response === 'True') {
          setMovies(jsonResponse.Search);
          setLoading(false);
        } else {
          setErrorMessage(jsonResponse.Error);
          setLoading(false);
        }
      });
  };


  return (
    <div className="App">
      <Header text="HOOKED" />
      <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;

上記のコードはeslint(react/prop-types)が発生する。

Do not nest ternary expressions. eslint(no-nested-ternary)

これは49-57行目で三項演算子入れ子にして使用しているため。

参考 disallow nested ternary expressions (no-nested-ternary)

JSX内では無名関数が使えるため、 無名関数内でIF文を使用してno-nested-ternaryを回避

import React, { useState, useEffect } from 'react';
import '../App.css';
import Header from './Header';
import Movie from './Movie';
import Search from './Search';

// 自分のAPI URLに置き換える必要がある
const MOVIE_API_URL = 'https://www.omdbapi.com/?s=man&apikey=4a3b711b';

const App = () => {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const [errorMessage, setErrorMessage] = useState(null);

  useEffect(() => {
    fetch(MOVIE_API_URL)
      .then((response) => response.json())
      .then((jsonResponse) => {
        setMovies(jsonResponse.Search);
        setLoading(false);
      });
  }, []);

  const search = (searchValue) => {
    setLoading(true);
    setErrorMessage(null);

    fetch(`https://www.omdbapi.com/?s=${searchValue}&apikey=4a3b711b`)
      .then((response) => response.json())
      .then((jsonResponse) => {
        if (jsonResponse.Response === 'True') {
          setMovies(jsonResponse.Search);
          setLoading(false);
        } else {
          setErrorMessage(jsonResponse.Error);
          setLoading(false);
        }
      });
  };

  return (
    <div className="App">
      <Header text="HOOKED" />
      <Search search={search} />
      <p className="App-intro">Sharing a few of our favourite movies</p>
      <div className="movies">
        { loading && !errorMessage
          ? (<span>loading...</span>)
          : (() => {
            if (errorMessage) {
              return (<div className="errorMessage">{errorMessage}</div>);
            }
            return (movies.map((movie) => (<Movie key={`${movie.Year}-${movie.Title}`} movie={movie} />)));
          })()}
      </div>
    </div>
  );
};

export default App;

上記のコードでeslint(no-nested-ternary)を回避できるが、 react/no-array-index-keyが発生する。

Do not use Array index in keys eslint(react/no-array-index-key)

react/no-array-index-keyは要素のkeyに配列のインデックスを使用すると発生するエラー。

配列がソートまたは追加されたとき、indexが変更され 不要なレンダリングが発生するから警告が発生するらしい。

参考 Prevent usage of Array index in keys (react/no-array-index-key)

適当に別のkeyを指定して修正

import React, { useState, useEffect } from 'react';
import '../App.css';
import Header from './Header';
import Movie from './Movie';
import Search from './Search';

// 自分のAPI URLに置き換える必要がある
const MOVIE_API_URL = 'https://www.omdbapi.com/?s=man&apikey=4a3b711b';

const App = () => {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const [errorMessage, setErrorMessage] = useState(null);

  useEffect(() => {
    fetch(MOVIE_API_URL)
      .then((response) => response.json())
      .then((jsonResponse) => {
        setMovies(jsonResponse.Search);
        setLoading(false);
      });
  }, []);

  const search = (searchValue) => {
    setLoading(true);
    setErrorMessage(null);

    fetch(`https://www.omdbapi.com/?s=${searchValue}&apikey=4a3b711b`)
      .then((response) => response.json())
      .then((jsonResponse) => {
        if (jsonResponse.Response === 'True') {
          setMovies(jsonResponse.Search);
          setLoading(false);
        } else {
          setErrorMessage(jsonResponse.Error);
          setLoading(false);
        }
      });
  };

  return (
    <div className="App">
      <Header text="HOOKED" />
      <Search search={search} />
      <p className="App-intro">Sharing a few of our favourite movies</p>
      <div className="movies">
        { loading && !errorMessage
          ? (<span>loading...</span>)
          : (() => {
            if (errorMessage) {
              return (<div className="errorMessage">{errorMessage}</div>);
            }
            return (movies.map((movie) => (<Movie key={`${movie.Year}-${movie.Title}`} movie={movie} />)));
          })}
      </div>
    </div>
  );
};

export default App;