同じ轍を踏まないように今まで React で出会ったエラーと警告を 覚えてる限りこのページに纏めていきます。
- info
- ES6 で書いてます。
react (react-dom)
setState メソッド がコンポーネントから切り離されたため、updater 属性が参照できない
- メッセージ
- TypeError: Cannot read property 'updater' of undefined
- 対応
- setStateのthisを固定する
- OK
- bind で this を固定する
fetch(url).then(res => res.json()).then(this.setState.bind(this))
- bind で this を固定する
- OK
- アロー関数の中で setState を実行する
fetch(url).then(res => res.json()).then(data => this.setState(data))
- アロー関数の中で setState を実行する
- 参考
ReactDOM.render の第二引数で指定したオブジェクトが DOM エレメントでない
- メッセージ
- Uncaught Invariant Violation: Target container is not a DOM element.
- 詳細
おそらくこれが発生するのは 取得しようとしたDOMが存在せず、
querySelector
やgetElementById
の返却値であるnull
が指定されてしまうというのが多いと思います。ReactDOM.render(<Container />, document.querySelector('#does-not-exist'))
- 対応
- 挿入する対象のDOMオブジェクトがちゃんと存在しているか確認しましょう。
特定のライフサイクルフック関数内で setState を実行したため無限ループが発生
正確には無限ループしそうになったので止めたというエラーです。 (setState の実行によってフックが呼び出されてしまうため)
- メッセージ
- Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
- 対応
- ライフサイクルフック内で setState を実行しない
- 特定の条件下でのみ setState を実行させるようにし、無限ループになることを防ぐ
何も書かれていない式が存在する
onClick={}
のように式に何も書かれていないとエラーが発生します
- メッセージ
- SyntaxError: JSX attributes must only be assigned a non-empty expression
- 対応
{}
内に式を記述する- 例)
onClick={() => console.log('clicked')}
一番外側のタグがラップされていない
隣接する要素は何らかのタグでラップする必要があります。
別の言い方をすると 式に指定する DOMオブジェクト は 一つでなければなりません。(逆にわかりにくかったらごめんなさい)
- メッセージ
- SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag
- 対応
- 配列で渡すかラップする(配列の場合はkeyが必要)
- NG
<span>a</span><span>b</span>
- OK
[<span key="a">a</span>, <span key="b">b</span>]
- OK
<div><span>a</span><span>b</span></div>
- OK
フラグメントを使う
<><span>a</span><span>b</span></>
イベントハンドラに 関数以外を指定した
- メッセージ
- Uncaught (in promise) Error: Invalid Argument Type, must only provide functions, undefined, or null.
- 詳細
- イベントハンドラに 関数以外を指定するとエラーになります。
null
やundefined
は受け付けるが何もしません (逆に問題に気づきにくいのでエラーにしてほしい気持ちはある)- よくある間違いとして
onClick={this.setState(data)}
のようにしてしまうと、 (setStateの)実行結果が null として格納されるためクリックしても何も起きない、ということが起こります。
- 対応
- イベントハンドラの式には関数オブジェクトを指定する
{}
の中で特定の関数を実行したいのであればアロー関数で覆う
- イベントハンドラの式には関数オブジェクトを指定する
- NG
<button onClick={this.setState(data)} />
- OK
<button onClick={() => this.setState(data)} />
undefined
を コンポーネントとして描画しようとした
- メッセージ
- Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
- 詳細
以下のように値が格納されていない変数を描画しようとすると発生します。
render () { let Test; return <Test /> }
実際にこれが起きるケースは import に失敗していることが多いです。 具体的には以下のようなケースです。
export
していないコンポーネントを取り込もうとしているdefault
エクスポートしていないコンポーネントを任意の名称で取り込もうとした
- 対応
export した コンポーネントを正しくインポートする
以下のようなコンポーネントがあったとして、NG,OKを見てみましょう
- ./mycomponent1.js
- ./mycomponent2.js
export class MyComponent extends React.Component { // なにか }
export default MyComponent2 exptends ReactComponent { // なにか }
- NG
import MyComponent1 from './mycomponent1'; // undefined
- NG
import {MyComponent2} from './mycomponent2'; // undefined
- OK
import {MyComponent1} from './mycomponent1';
- OK
import MyComponent2 from './mycomponents2';
- OK
import {default as MyComponent2} from './mycomponents2';
描画しようとしているオブジェクトの型が適切でない
- メッセージ
- Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {x, y, z}). If you meant to render a collection of children, use an array instead.
- Uncaught Error: Objects are not valid as a React child (found: object with keys {xxxx}). If you meant to render a collection of children, use an array instead.
- 詳細
- もし子要素のまとまりを描画したいならオブジェクトではなく 配列で渡します。
- React が式として描画できるのは スカラ値(文字,数値,NULLなど) か
ReactDOM オブジェクト、またはそれらを子とする配列です。
(他にもあったかな?) いずれにせよオブジェクト(
{}
)は描画できません。 - 私の環境でこれが発生したのは引数の順番を変更した結果、描画用の引数で期待しないオブジェクトを受け取っていたためでした。
react-dom がない
- メッセージ
- Uncaught ReferenceError: ReactDOM is not defined
- 詳細
- react-dom がない (主にインポートし忘れていると) ときに発生します
- React 0.14 以前では
React.render()
を使っていたが、 それより後のバージョンではReactDOM.render()
を使うようになったため、移行のタイミングで発生しやすいかも。
- 対応
react-dom を インポートする
import ReactDOM from 'react-dom'
- 参考
エラーが発生したが開発モードでないため詳しいエラー内容を表示できない
- メッセージ
- Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
- 詳細
- Reactが開発モードで動かしていないのでエラーの詳細が表示されないというメッセージです
- 対応
- Webpack を使っている場合は
webpack.config.js
のmode
にdevelopment
を指定する。 - 本番環境の場合は、本当のエラーを解決した後に
"production"
としてください。
- Webpack を使っている場合は
render メソッドから 何も返却されない
- メッセージ
- Uncaught (in promise) Error: Component(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
- 詳細
- render メソッドから 何も返却されない
(正確に言うと
undefined
が返却されている)ためにエラーが発生しています。 - 主に
return
が欠けている場合に発生するとメッセージには書いてありますが、 私の場合は return に何も渡していないために発生していました。
- render メソッドから 何も返却されない
(正確に言うと
- 対応
- 意図して何も描画しない場合は
return
だけでなくnull
を明示的に返却しましょう。
- 意図して何も描画しない場合は
- NG
(もしくは return 文がない)
return;
- OK
return null;
フォーム要素のコントロール状態が途中で変化した
- メッセージ
- Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
- 詳細
- state で value や checked などの状態を管理している
フォーム要素を
controlled
といい、逆に管理していない状態をuncontrolled
といいます。 uncontrolled
だったコンポーネントが途中でcontrolled
に変わったという警告で、 この性質は一貫するべきということらしいです。 (controlled
からuncontrolled
もだめ)- そんなの変更してる覚えはないわけですが、初期値が
null
やundefined
になった場合、 Reactはuncontrolled
と判断してしまいます。 - そして、サーバからのレスポンスやユーザの入力があると
null
やundefined
ではなくなるのでcontrolled
になってしまうわけです。
- state で value や checked などの状態を管理している
フォーム要素を
- 対応
- null にならないように 別の値に差し替える。文字列フィールドの場合は空文字 など。
- NG
<input value={this.state.value} />
- OK
<input value={this.state.value || ''} />
- 参考
「render メソッド」 や 「他コンポーネントのコンストラクタ」で state の変更があった
- メッセージ
- Warning: Cannot update during an existing state transition (such as
within
render
or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved tocomponentWillMount
.
- Warning: Cannot update during an existing state transition (such as
within
- 詳細
- 「render メソッド」 や 「他コンポーネントのコンストラクタ」で state の変更があったという警告です。
- 前者は
setState
によって更新を検知すると render が呼び出され、無限ループする恐れがあるためです。 - 実際に無限ループしてしまうと
Maximum update depth exceeded
エラーになります。 - 他コンポーネントのコンストラクタでの状態更新はアンチパターンのようです。
これが発生するのは
setState
(あるいはsetState
を内部的に呼び出す関数) を props として渡している場合が多いと思います。
- 対応
- render メソッドやそこから呼び出される関数内で setState を呼び出さない。
- コンストラクタ で 呼び出している setState を
componentWillMount
メソッドに 移動する。componentWillMount
メソッド は初期化に適しているそうです。
- 参考
- info
- 参考リンクに書いてありましたが、 BrowserHistory の push メソッドの実行も 状態変更を引き起こすようです
DOM の入れ子(ネスト)関係が不正
- メッセージ
Warning: validateDOMNesting(...): \<tr> cannot appear as a child of \<table>. Add a \<tbody> to your code to match the DOM tree generated by the browser.
Warning: validateDOMNesting(...): \<a> cannot appear as a descendant of \<a>
Warning: validateDOMNesting(...): \<li> cannot appear as a descendant of \<li>.
Warning: validateDOMNesting(...): \<th> cannot appear as a child of \<thead>.
Warning: validateDOMNesting(...): \<td> cannot appear as a child of \<tbody>.
- 詳細
- DOMの入れ子(ネスト)関係が不正。コンポーネントを入れ子にすると発生しやすいです。
- tr は table タグの直下に置くべきではない (thead, tbodyの下に置くべき)
- aタグ(link) の 中に a タグが含まれているべきではない。
- li タグは ul タグの子要素になるべきだが、 li 要素の下に描画されてしまっている。
- th タグは tr タグの子要素になるべきだが thead 要素の下に描画されてしまっている
- td タグは tr タグの子要素になるべきだが tbody 要素の下に描画されてしまっている
- DOMの入れ子(ネスト)関係が不正。コンポーネントを入れ子にすると発生しやすいです。
- 対応
- 入れ子の関係を正しくする
- table タグ直下 にある tr タグは tbody タグで囲む
- a タグは a タグの子孫要素に表示しない
- li タグの親タグは ul にする (ul タグの子に li タグ以外を指定しない
- td, th タグは tr タグで囲む
- 入れ子の関係を正しくする
イテレーション内で描画されているDOMに key
属性が指定されていないか属性値が重複している
- メッセージ
- Warning: Each child in an array or iterator should have a unique "key" prop.
- 詳細
- イテレーション内で描画されているDOMに
key
属性が指定されていないか属性値が重複しているという警告です。 - mapの中で生成する場合であっても、配列内のDOMをそのまま表示する場合であっても key は必要なので注意してください。
- イテレーション内で描画されているDOMに
- 対応
- 該当箇所の発見が地味に面倒くさいですが、トレースバックを追えばなんとか該当箇所はわかります
- あとは コードに 一意な key 属性を指定するだけです。
- NG
<div>{['a', 'b', 'c'].map((v) => <div>{v}</div>)}</div>
- NG
<div>{ [ <div>a</div>, <div>b</div>, <div>c</div>, ] }</div>
- OK
<div>{['a', 'b', 'c'].map((v, i) => <div key={i}>{v}</div>)}</div>
- OK
<div>{ [ <div key="a">a</div>, <div key="b">b</div>, <div key="c">c</div>, ] }</div>
マウントされていないコンポーネントで setState や replaceState を行ったので何もしなかった
- メッセージ
- Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
- Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
- Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
- 詳細
- メモリリークを引き起こす可能性があるので修正したほうがいい(らしい)です。
- 対応
- コンポーネントがマウントされてから 更新メソッドをコールします。 多分コンポーネント間通信をしてるときに発生するんじゃないかな。
class 構文を使って getDefaultProps
を定義した
- メッセージ
- Warning: getDefaultProps was defined on MyComponent, a plain JavaScript class. This is only supported for classes created using React.createClass. Use a static property to define defaultProps instead.
- 詳細
- (メッセージによると)これは
React.createClass
を使ってコンポーネントを作るときに使われるものだそうです。
- (メッセージによると)これは
- 対応
- クラス構文を使って書く場合は
defaultProps
プロパティを定義すればOK。
- クラス構文を使って書く場合は
- NG
class MyComponent extends React.Component { getDefaultProps () { return {a: 1} } // なにか }
- OK
class MyComponent extends React.Component { // なにか } MyComponent.defaultProps = { a: 1 }
- OK
- class-properties が使える場合(babel-plugin-transform-class-properties)
class MyComponent extends React.Component { static defaultProps = { a: 1 } }
- 参考
defaultProps
プロパティをメソッドとして定義してしまった
- メッセージ
- Warning: Setting defaultProps as an instance property on MyComponent is not supported and will be ignored. Instead, define defaultProps as a static property on MyComponent.
- 説明
defaultProps
プロパティを メソッドとして定義してしまった場合に発生します。 メソッドではなくオブジェクトとして定義するべきです。
- 対応
- これの一つ上の Warning をご確認ください。
checked状態を変化させる手段がない
- メッセージ
- Warning: Failed prop type: You provided a
checked
prop to a form field without anonChange
handler. This will render a read-only field. If the field should be mutable usedefaultChecked
. Otherwise, set eitheronChange
orreadOnly
.
- Warning: Failed prop type: You provided a
- 詳細
checked
が指定されているにもかかわらず、それを操作するためのonChange
ハンドラを使っていないため、チェックボックスをクリックしても変更できない(つまりReadOnly)ということを React が心配してくれてる Warning です。
- 対応
初期のチェック状態だけを与えて、チェックボックスの ON/OFF 操作は有効にしたい場合は
checked
props の代わりにdefaultChecked
を使いましょう。それ以外の場合は、
onChange
かreadOnly
props を明示します。
- NG
// checked の状態を state で管理しているが onChange が提供されていない render () { return ( <input type="checkbox" checked={this.state.checked} /> ) }
- OK
// checked の初期値を defaultChecked で渡しているが状態は管理していない render () { return ( <input type="checkbox" defaultChecked={this.state.checked} /> ) }
- OK
// checked の状態を state で管理しており onChange も提供している render () { return ( <input type="checkbox" checked={this.state.checked} onChange={() => this.setState({checked: !this.state.checked})} /> ) }
- OK
// input の checked の状態を state で管理しており、readOnly を明示している render () { return ( <div> <button onClick={() => this.setState({checked: !this.state.checked})} >{this.state.checked ? 'ON' : 'OFF'}</button> <input // この input 要る?とかいうツッコミをしてはいけない type="checkbox" style={{display: 'none'}} checked={this.state.checked} readOnly /> </div> ) }
- info
- 最後の例のように チェックボックスを別の場所から操作する場合、
readOnly
props を指定すればOKです。
- 最後の例のように チェックボックスを別の場所から操作する場合、
DOMの属性がキャメルケースで記述されていない
- メッセージ
- Warning: Invalid DOM property
class
. Did you meanclassName
? - Warning: Invalid DOM property
colspan
. Did you meancolSpan
? - Warning: Unsupported style property font-weight. Did you mean fontWeight?
- Warning: Invalid DOM property
- 詳細
- DOMの属性は Reactが期待する記法(キャメルケース)で記述する必要があります。
- 対応
class
属性はclassName
とする必要がある- td タグの
colspan
属性はcolSpan
とする必要がある (他にもありそう) - CSS の プロパティ は ケバブケース (
-
区切り) ではなく、キャメルケースで記述する
以下は react 関連のサードパーティライブラリ で発生した問題です
redux (react-redux)
Storeオブジェクトが正しいReducerを持っていない
- メッセージ
- Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.
- 説明
Store オブジェクトが正しい Reducer オブジェクトを持っていないために発生しています。
combineReducers
に Reducer を与えていない場合に発生するようです。(他に発生するケースが有るのかな?わかったら追記します
- 対応
- combineReducers に Reducer オブジェクトを指定します。
- NG
const reducer = combineReducers({});
- OK
import user from './user'; // 環境によって変える const reducer = combineReducers({ user });
- info
- 新しいのに遭遇したら追記します。
- これも追記してくれみたいな要望をコメントでくれると後で追加するかもしれません。(その場合は発生条件とかも書いてくれるとうれしい)
- 使い方の質問やバグはライブラリの方へISSUEを出してください。(一応)
良いReactライフを! 間違いがあったらやさしく指摘ください。