1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Remix】遭遇したエラーと対処法メモ

Posted at

Remixの開発で遭遇したいろんなエラーとその対処法のメモ。個人の備忘録に近い。同じことで悩んでいる誰かの役に立てばいいなと思いここに記録残します。

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

各ページのexport defaultのReact Component Functionにasyncつけてるとこのエラーになる。loaderasyncつけてたのでその流れで無意識にexport default async function Hogehoge()とか書いてしまっていたが、各ページのexport default functionにはasyncつけてはいけない。

Error: You defined an action for route "routes/hogehoge.tsx" but didn't return anything from your action function. Please return a value or null

これはメッセージの通りである。action()を定義しているが何もreturnしてませんよ、何かしらreturnするか、少なくともnullをreturnしなきゃだめですよ、ということを指摘している。これは実際その通りで、「サーバー処理し終わったら何事もなく自画面に戻ってきてくれればそれでいいや」という感じだったので、特に何もreturnしなかった(というかそういう発想がなかった)のだが、ただまあその程度であり、強い意図はなかった。そういう場合でも少なくともnull返せというのがRemixの仕様らしい。はい。

Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

map使って明細描画してるところでmap内のロジックでuseState使ってる場合に、明細そのものの数が増減するアクションを実行するとこのエラーになる。要するにuseStateをトップレベル以外で(動的に)使うとこうなる。こう書くと当たり前の話で素人みたいだ。。。ちなみに以下のような感じである

export async function loader({request}:LoaderFunctionArgs) {
    const searchResult = await getItems(request);
    return {searchResult: searchResult};
}
export default function Page() {
    const data = useLoaderData<typeof loader>();
    return (
        <div>
            <Form>
                <input type="date" name="search_date" required />
                <button>
                    search
                </button>
            </Form>
            {data.searchResult.map((s)=>{
                const [name, setName] = useState('');
                const getName = async () => {
                    const fetchResult = await fetch('https://hogehoge.com/get-name');
                    const fetchResultJson = await fetchResult.json();
                    setName(fetchResultJson.name);
                };
                return (
                    <div key={s.id}>
                        <div>
                            {s.id}
                        </div>
                        <div>
                            {name !== '' ? <p>{name}</p> : <></>}
                        </div>
                        <div>
                            <button onClick={getName}>
                                get name
                            </button>
                        </div>
                        
                    </div>
                );
            })}
        </div>
    );
}

この例だとconst [name, setName] = useState('');data.searchResult.map((s)=>{...の中におり、最初の検索のときにはまだ動作するが、search_dateを変えて検索して再描画し、検索結果(searchResult)の件数が変わると、表題のエラーになる。useStateを一番上に持ってこないとだめ(ただこの場合はuseState後の描画のリフレッシュの考慮が多分必要になる)。あるいはこの部分(<button onClick={getName}>get name</button>getName()setNameするところ)をコンポーネントとして別に切り出すかする必要がある。

TypeError: Cannot convert argument to a ByteString because the character at index XX has a value of YY which is greater than 255.

XXとYYには数値が入る。これはredirectの引数に渡すURLの文字列に全角文字などのマルチバイトキャラクターをURLエンコードせずにそのまま渡すと発生する。return redirect('/hogehoge?param=ほげほげ');のようなもの。対策は簡単で、事前にencodeURIすればいいだけ。

const uri = encodeURI('/hogehoge?param=ほげほげ');
return redirect(uri);

versionなど

    "@remix-run/node": "^2.16.6",
    "@remix-run/react": "^2.16.6",
    "@remix-run/serve": "^2.16.6",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
$ node --version
v20.18.1
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?