質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.86%

  • JavaScript

    14764questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • React.js

    654questions

    Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

  • Material-UI

    14questions

JSのみで投稿サイトのビュー

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,720

MOTOMUR

score -41

javascriptとreact、material-uiを用いて、アプリ作成しています。

現在のプロジェクトはこちら。

テストサーバーはreact-scripts startにて。
ホスティングサーバーはfirebaseにて。

課題点(要素の多さによる遅延の軽減)

現在、componentDidMountにて、たくさんのfetchや処理が混雑していて、遅延によりページが表示できないことが増えてきました。

これを解消したいです。

どのように復活させられるでしょう?

<今後実装したいが搭載に困っている機能>
①多言語化(とりあえず英語と日本語)
②デザイニング

解決できるかたからのメッセージまたはGitへの直接の書き込み待ってます。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2017/11/21 08:56

    複数のユーザーから「やってほしいことだけを記載した丸投げの質問」という意見がありました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 2

checkベストアンサー

+2

そもそもjs上だけで動くものを作りたい

jsxは使いたくないということですか?
jsでテンプレートを書きやすくするためにちょっとだけjsを拡張したものなので、すぐに学習できると思います。

jsxはreactのランチャーから行くと、たくさんのエラーを吐きました。

エラーメッセージを提示していただくと解決につながるかもしれません。

この辺りの機能の名前がわからなくて

  • ユーザー認証 -> authentication
  • ゲスト、ユーザーが見れるプラットホーム -> 普通のウェブアプリ?(わかりません)
  • ユーザー課金、ゲスト課金 -> payment / 決済

ちなみにnode_modulesは.gitignoreに入れたほうがいいですよ。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/10 23:48

    回答ありがとうございます!jsxに対して不信感がなくなり、勉強しようと思いました。そこで、方針を参考にしたブログのjsxの構文を使ってみることにしました。しかし、エラーを吐きます。このSyntaxエラーはエラー文ベタ塗りで検索かけてもよくわかりませんでした。

    キャンセル

  • 2017/10/10 23:52

    追記:.gitignoreの件、アドバイスありがとうございました!node-moduleを除外し、コミットしました。間違いがあれば指摘してもらえると幸いです。

    キャンセル

  • 2017/10/10 23:52

    syntax errorなら、どのファイルの何行目の何文字目、というのが表示されると思いますので、それを参考に修正できると思います。

    キャンセル

  • 2017/10/11 00:00

    @の検索が、異常に難しく、解決が難しかったです。       ・@はjsx上の構文でしょうか? その場合、@を受け入れられるLintをインストールしなくてはいけないでしょうか?

    キャンセル

  • 2017/10/11 00:02

    消したら、一応コンパイル通ったので、実行して見ます。通知をなんどもすいません。

    キャンセル

  • 2017/10/11 00:15

    ブログにあったサンプルは、reduxも使用しているので、reduxとreact-reduxを追加しました。
    import文のfrom react/redux はreact-reduxだと思って修正しましたが、致命的にたくさんのエラーを吐きギブアップ状態です。。エラーのうちの1例としては、怒られてなかったindex.jsの 「ReactDOM.render(<App />, document.getElementById('root'))」まで怒られる始末です。

    キャンセル

  • 2017/10/11 00:20

    @はデコレーターというもので、jsの最新機能?です。それ自体はjsx特有のものではありません。
    connectはreduxの関数であり、reactのcomponentをreduxのstoreとつなげるものです。
    恥ずかしながらreduxについてはほとんど知らないのでお答えしにくいですが、デコレーターはbabelのconfigをちゃんとすればsyntax errorにならないはずです。

    この記事がconnectを説明してます。
    https://mae.chab.in/archives/2885

    キャンセル

  • 2017/10/11 00:22

    reduxは無理して使わなくてもいいと思います。あくまでappどこからでもアクセスできるデータのシステムを提供するものです。

    キャンセル

  • 2017/10/11 00:28

    デコレーターの詳細はこの記事が参考になるのではないでしょうか。
    https://www.webprofessional.jp/javascript-decorators-what-they-are/

    キャンセル

  • 2017/10/11 00:28

    今後、必要かどうかは置いておいて、Reduxを勉強しようと思います。 しかし、今回のアプリ作成の目的は、react.jsとmaterial-uiの最小単位で、早く完成させることなので、とりあえずアドバイス通りReduxは放置します。さて、ここで新たに問題なんですが、Reduxのconnectを外した場合、そこに変わるコンポーネントが必要なんですが、全くどう変えていいか、見当もつきません。
    代わりとなるものはすぐに作れるものでしょうか?

    キャンセル

  • 2017/10/11 00:39

    そこに変わるコンポーネントとはどういうことでしょうか?
    class UserOnly自体コンポーネントなのでそれを残して、
    @connect(state => ({
    session: state.session
    }))
    を削除すればいいのではないでしょうか。

    キャンセル

  • 2017/10/11 00:44

    バカで申し訳ないです!消して実行しましたが、Reduxの爪痕でエラーがたくさんあるので、修正して試して見ます。

    キャンセル

  • 2017/10/11 01:06

    ./src/components/GuestOnly.jsx
    Line 5: React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead react/no-deprecated
    とエラーになってしまいます。

    キャンセル

  • 2017/10/11 01:16

    React.PropTypesがReact 15.5.0から廃止になり、prop-typesという別ライブラリになったということです。使用するにはnpmでインストールする必要があります。

    PropTypesは文字通りコンポーネントのpropのデータ型をチェックしてくれる、開発用の機能です。
    なのでそれがいらないなら使う必要はありません。

    React.PropTypesからprop-typesへの移行の仕方
    https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactproptypes

    キャンセル

  • 2017/10/11 11:24

    ありがとうございます!PropTypesは修正できました!
    ターミナル上ではコンパイルできるんですが、ランチャー上で怒られます。
    エラーを本文に追記します。

    キャンセル

  • 2017/10/11 11:29

    すみませんがランチャーとは何のことでしょうか...。"react-scripts start"のことですか?

    キャンセル

  • 2017/10/11 11:35

    それです!ごめんなさい!ランチャーって覚えちゃってました。修正します。

    キャンセル

  • 2017/10/11 11:49

    エラーですが、
    TypeError: Cannot call a class as a function
    UserOnly
    とあるので、たぶんUserOnlyクラスを"UserOnly()"みたいに関数として使ってしまったのが原因ではないでしょうか。

    キャンセル

  • 2017/10/12 11:31

    ユーザー認証はFirebaseでやってみることにしました。現在git commitで苦戦中

    キャンセル

  • 2017/10/12 14:05

    precommitとかjestは全然知らないのですが、
    collectCoverageFrom とかは jest のコンフィグオプションらしいですがそれがpackage.jsonで変なところに入ってるのではないでしょうか。

    キャンセル

  • 2017/10/12 14:21

    解決しました。ありがとうございます。package.json のprecommitが悪さしていたようです。
    Firebase導入に苦戦しているのですが、material-uiのテキストから受け取った情報を変数(?)に入れて、 firebase.auth().signInWithEmailAndPassword(email, password) このメールとパスワードに渡したいんですが、やり方がわかりません。。。。

    キャンセル

  • 2017/10/12 15:01

    material-uiは使ったことがないですが、reactのコンポーネントのライブラリなんですかね。

    普通のinput要素と同様に、Controlled componentにすれば、テキストボックスに入ってるテキストとコンポーネントの変数の間で同期?できます。
    https://reactjs.org/docs/forms.html#controlled-components

    TextFieldコンポーネントを使っていますか?
    http://www.material-ui.com/#/components/text-field
    このページのControlled exampleのとことか参考になるのではないでしょうか。右にある"<>"をクリックすればソースが見れますよ。

    キャンセル

  • 2017/10/12 17:18

    ありがとうございます。使い方は何と無くわかりました。http://www.material-ui.com/#/components/text-fieldのControlled exampleの
    constructor(props) {
    super(props);

    this.state = {
    value: 'Property Value',
    };
    }
    とか、なんのために使ってて、なにをしてるのかわからなくて困ってます。

    キャンセル

  • 2017/10/12 17:19

    詳しく教えてくれてるサイトとかも見つけられないので。お願いします。

    キャンセル

  • 2017/10/12 17:54

    react componentの概念は理解なさっていますか?
    componentにはpropsとstateがあります。
    公式
    https://reactjs.org/docs/state-and-lifecycle.html
    https://reactjs.org/docs/components-and-props.html
    日本語解説
    https://qiita.com/kuniken/items/a22adda392ccc30011b1
    https://qiita.com/kyrieleison/items/78b3295ff3f37969ab50

    constructorはclassのコンストラクタ(そのまま)です。
    インスタンスを作成するときに呼び出されます。
    C#とかJavaでもコンストラクタ内でsuper(上位クラス)のコンストラクタを呼び出す、ということはよくあると思いますが。
    日本語解説
    http://www.yunabe.jp/docs/javascript_class_es6.html

    キャンセル

  • 2017/10/12 22:59

    state props勉強してきました。不明点は質問文を編集しました。お力添えをお願いします。

    キャンセル

  • 2017/10/12 23:28

    firebase.auth()... をクラスの外にそのまま書いてますが、これだとこのjsファイルが読み込まれた時点で実行されてしまうのでまずいのではないですか?

    キャンセル

  • 2017/10/12 23:31

    自分のイメージだと、class内に入ってるように感じるんですけど、もしかしてクラス内の判定はrender()の中!?!?

    キャンセル

  • 2017/10/12 23:33

    中にしたら怒られませんでした。 emailとpasswordがnot definedみたいです。どこかで初期設定必要なんですかね。

    キャンセル

  • 2017/10/12 23:35

    今気づきましたが"}"が最後欠けてませんか?

    キャンセル

  • 2017/10/12 23:36

    最後というか、一番外側の{}のペアが、}が欠けてると思うのですが。

    キャンセル

  • 2017/10/12 23:37

    現在のコードに編集しました。

    キャンセル

  • 2017/10/12 23:40

    現在の怒られることは、 emailとpasswordがnot defined

    キャンセル

  • 2017/10/12 23:46

    すみません言うのが遅れましたがrender()の中に書くのはおかしいというか、使い方がよくありません。
    emailとpasswordはユーザーに入力させるんですよね?
    いつ認証(firebase.auth()....)させたいんですか?

    キャンセル

  • 2017/10/12 23:48

    ちなみに、そのコンポーネントのstateにアクセスするには
    this.state.<変数名>
    です。render関数内でも使われていますよね?

    キャンセル

  • 2017/10/12 23:49

    入力後に、ログインボタンを押したタイミングでですね。
    ログインボタン追加してきます。
    ログインボタンの入力判定も handleChange = (event) => {
    this.setState({
    ???
    });
    };
    みたいな感じでできますかね_?

    キャンセル

  • 2017/10/12 23:51

    とりあえずボタンだけ追加しました。

    キャンセル

  • 2017/10/12 23:58

    さらに修正

    キャンセル

  • 2017/10/12 23:59

    ボタンの入力も同様にできます。setStateする必要があるかは知りませんが。

    handleChangeがどこにも使われていないので意味がないですね...。
    もう一度↓のControlled exampleのソースを見て、コンポーネントの変数とTextFieldの値が同期されるようにしてみてください。重要なのはvalueとonChangeをpropsで渡しているところです。
    http://www.material-ui.com/#/components/text-field

    イベントハンドラー(今で言うhandleChange)は別々に作ったほうがいいと思います。

    キャンセル

  • 2017/10/13 00:01

    現在エラー:not defined  email password

    キャンセル

  • 2017/10/13 00:07

    handleChange別に作ったり、色々変更しました。

    キャンセル

  • 2017/10/13 00:24

    コンパイルできるようになりましたが、firebaseのところと、index.jsのReactDOM.render(<App />, document.getElementById('root'))がダメと、react-scripts start 上にてダメになります。

    キャンセル

  • 2017/10/13 00:27

    細かい気になる点がいろいろあるので、Demoを作りました。
    https://codesandbox.io/s/2vmwym63r
    (ボタンクリック時に認証処理の代わりに、単にemailとpasswordをconsole.logしています)

    TextFieldに password={this.state.password} など属性を指定していますが、これは意味がありません。TextFieldの値は"value"によって渡す、と公式ページに書いてあります。

    また2つのTextFieldに同じidを与えていますが当然HTMLとしてよくありません。

    最後に、RaisedButtonの onClick に
    onClick={firebase.auth().signInWithEmailAndPassword(email, password)}
    と直接やってほしいことを書いていますが、これは間違っています。
    ここで渡すべきは関数です。関数を渡せば、クリック時にその関数が実行されます。
    TextFiledと同様に一つイベントハンドラーの関数を作るといいです。

    キャンセル

  • 2017/10/13 00:35

    ありがとうございます。アドバイス通り修正しました。このエラーが謎です。Syntax error: Unexpected token, expected , (31:14)

    29 | handleChange_button = (event) => {
    30 | this.setState({
    > 31 | firebase.auth().signInWithEmailAndPassword(this.state.email, this.state.password)
    | ^
    32 | });
    33 | };
    34 |

    キャンセル

  • 2017/10/13 00:42

    jsのsyntaxはあまりご存知ないのでしょうか?
    { key: value } はオブジェクトリテラルというもので、このsyntaxに合ってないということです。
    そもそも今 setState する必要はあるのでしょうか?
    setState は state を更新する関数ですが、理解なさっていますか?

    キャンセル

  • 2017/10/13 00:56

    あまり詳しくないです。しかし、このsetStateに関してはコピー後の消し忘れです。申し訳ありません。無事コンパイルでき、onClickまでは動作するようになりました。

    キャンセル

  • 2017/10/13 01:00

    度々の稚拙な質問にお答えくださり、感謝しかありません。ご容赦を。

    キャンセル

  • 2017/10/13 01:10

    いえいえ。Nodejsでいろんなライブラリを使って開発するのは最初は誰でも混乱すると思います。
    ただ、こういったwebアプリの開発はjsやhtmlの基礎をしっかり勉強してから始められた方が、つまづきにくいかと思います...。

    キャンセル

  • 2017/10/13 13:22

    ルーティングに困っています。Loginをクリック後に、ファイアーベース起動、ログイン、ログイン状態をuserに落とし、それで条件分岐しています。react-routerのrender外での指定パスに飛ばす方法が調べてもいまいちわかりません。。。。

    キャンセル

  • 2017/10/13 13:45

    「react-routerのrender外での指定パス」というのは何のことでしょうか?
    App.js で <Switch> 内に <Route>がいくつかありますが、これの一つに飛ぶということですか?

    以下細かい点
    - event.target.email/password ではなく event.target.value です。

    - firebase.auth().onAuthStateChanged をクリックするたびに実行するのはよくありません。あくまでこれはイベントハンドラを設定する関数なので、コンポーネントが表示されるときに一回実行すればいいと思います。
    ライフサイクルメソッドの componentWillMount() か componentDidMount() を使えばできます。
    https://reactjs.org/docs/react-component.html#componentwillmount

    キャンセル

  • 2017/10/13 22:03

    <Route>のうちの一つに飛ばしたいということです。 componentWillMount() と componentDidMount()  を自分なりに解釈し、コード訂正しました。間違いあれば教えてください。

    キャンセル

  • 2017/10/13 22:44

    demoを更新しました。
    https://codesandbox.io/s/2vmwym63r
    Loginページのボタンを押すとHelloページに飛ぶようになってます。

    <Route component={##}/>
    で指定したcomponentには history オブジェクトが props として渡されますので、その push メソッドで任意の path に飛べます。

    history object
    https://reacttraining.com/react-router/web/api/history

    キャンセル

  • 2017/10/13 22:51

    demoのログインボタンを押してもHelloページに飛べませんでした泣 historyの説明を読んでいますが、解読中。

    キャンセル

  • 2017/10/13 22:55

    あっすみません。Login.jsをセーブし忘れました。
    更新したのでもう一度ご覧ください。

    キャンセル

  • 2017/10/13 23:08

    Routehistryの使い方。理解できました。コード追加しました。console.logで確認したところ、componentDidMount()に到達していないようです。componentDidMount()はボタン押したら反応するのは認識間違いでしょうか?

    キャンセル

  • 2017/10/13 23:11

    それともonButtonClick内に条件分岐等全部入れてしまっていいのですかね?

    キャンセル

  • 2017/10/13 23:16

    公式ページを見れば分かりますが、
    https://reactjs.org/docs/react-component.html#componentwillmount
    componentがマウントする(ページ上に作成される)直前に呼び出されます。

    キャンセル

  • 2017/10/13 23:18

    あ、componentDidMountでしたね。間違えました。
    そっちはマウントされた直後に呼び出されます。

    キャンセル

  • 2017/10/13 23:22

    先述の通り firebase.auth().onAuthStateChanged はイベントハンドラを設定する関数です。なのでコンポーネントが作成されるときに一回やればいいということです。

    キャンセル

  • 2017/10/13 23:30

    ログインボタンを押して、ログイン完了した時にはホームに、できなかったらログインページにとしたいのですが。この場合はボタンを押されたタイミングに、ログイン状態をfirebase.auth().onAuthStateChangedで取得して、条件分岐するっていう処理と。ページが開かれて、コンポーネントが作成される直前にログイン状態を確認して、ログイン状態なら、ログインページに来た瞬間にホームに飛ばすっていう二つの処理が必要って感じですかね?

    キャンセル

  • 2017/10/13 23:34

    コードをボタン押した時の処理だけ追加しました。ページ遷移直後は未実装。エラー内容はログインページへ飛ばすpushのprops

    キャンセル

  • 2017/10/13 23:54

    2つの処理が必要という意味ではありません。
    firebase.auth().onAuthStateChanged(f) は使い方でいうと btn.addEventListener("click", funtion () { ... });
    と同じです。ロードされた時に、最初に1回だけやればいいのです。

    非同期処理というのをご理解されていないような気がします。
    ボタンを押した時に
    firebase.auth().signInWithEmailAndPassword( ... );
    を実行しますね?その後、認証が完了するのはいつですか?
    この行が実行された直後ですか?いいえ違います。
    いつ完了するか分かりません、というのが答えです。
    なのでイベントハンドラーを設定するのです。

    コンポーネントが作成されたときに
    firebase.auth().onAuthStateChanged(f)
    としておけば、認証が完了したときに f が実行されます。

    キャンセル

  • 2017/10/14 00:04

    なるほど!!!!やっと理解しました。ありがとうございます!自分のコードを修正しました。修正したところ、TypeError: Cannot read property 'history' of undefinedとなるのですが、historyはインポートしないと使えなかったり、どこかで定義しなければならないのでしょうか?import history〜 とするとhistoryどこにも使ってないと言われるし。調べてもよくわかりません。

    キャンセル

  • 2017/10/14 00:07

    firebase.auth().onAuthStateChanged(function(user){
    if(user){
    this.props.history.push('/')
    }
    else {
    this.props.history.push('/login')
    }
    })

    の props が undefined になる理由ですが、一般に関数の中に入ると"this"が指すものは外側での"this"と異なります。
    なので関数の外側で
    const self = this;
    と退避して、関数の内側で
    self.props.history...
    とすればうまくいくと思います。

    this についても調べておくとよいかと思います。

    キャンセル

  • 2017/10/14 00:25

    うまく動くようになりましたありがとうございます。まだFirebaseのログアウトを実装しておらず、ログインしてない時にログインのビューに飛べるかどうかわからなくなってしまったのですが、確認していただけますでしょうか。gitに現在のコードをのせました。ログイン用のデモアカウントはID admin@admin.com PW adminn です。

    キャンセル

  • 2017/10/14 00:29

    確認できました。変なこと頼んで申し訳ないです。

    キャンセル

  • 2017/10/14 00:30

    簡易ログアウトボタン実装し他コードをgitしておきます

    キャンセル

  • 2017/10/14 00:30

    パスワードとかここに載せないほうがいいと思いますが...。

    キャンセル

  • 2017/10/14 00:32

    確認用IDpsですので。管理できているので大丈夫です。すいません笑

    キャンセル

  • 2017/10/14 00:45

    ヘッダーのログインボタンとログアウトボタンの管理についてどうやっていいかわかりません。。

    キャンセル

  • 2017/10/14 00:47

    後、これは余力があればやりたいのですが、ログインページでヘッダーのログインボタンを消す方法もわかると嬉しいです。。

    キャンセル

  • 2017/10/14 01:11

    「ヘッダーのログインボタンとログアウトボタンの管理」
    管理というと具体的に何でしょうか?

    キャンセル

  • 2017/10/14 01:13

    質問でも更新しているのですが、ログイン中はログアウトボタンを表示して、ログインしていない状態だとログインボタン。ログインページではログインボタンもログアウトボタンも表示しない。という管理をしたいです。

    キャンセル

  • 2017/10/14 02:07

    条件によって表示したりしなかったり(conditional rendering)はカンタンです。
    コンポーネントのrender関数でif文を使うもよし、三項演算子を使うもよし。
    https://reactjs.org/docs/conditional-rendering.html
    demo
    https://jsfiddle.net/b1z01b34/

    ..しかしやはり redux が欲しくなって来ますね。アプリ全体でログイン情報にアクセスしたいので。

    キャンセル

  • 2017/10/14 12:05

    reduxあった方が楽そうですね。redux導入を先にします。

    キャンセル

  • 2017/10/14 12:22

    うーん難しいRedux。

    キャンセル

  • 2017/10/14 12:41

    reduxの勉強にあたり、https://qiita.com/gcmae/items/e85abecd999257c2ca8cこの記事を読んで、理解しようとしているのですが、他にいいサイトとかありますか?

    キャンセル

  • 2017/10/14 15:29

    私はいつも公式ページを見るのが一番いいと思ってるいるのですが、英語だと理解しにくいというならこの記事とか図がいっぱいあっていいんじゃないですかね。
    https://qiita.com/kiita312/items/49a1f03445b19cf407b7

    firebaseについてですが、ここを見ると
    https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
    いつでも firebase.auth().currentUser で現在のユーザーを取得できるそうです。
    なので、もし(ログインページ以外で)ログイン状態が変化したときにreactに自動的に表示を更新してほしい、というのでなければ redux は必要ないかもしれません。

    キャンセル

  • 2017/10/14 22:44

    firebaseベースでログイン状態管理することにしました。不明なのはcomponentWillountをコンポーネント作成前に走らせて、userを定義して、render内で使っているつもりなのに、render内のuserがnot definedなことです。

    キャンセル

  • 2017/10/14 22:48

    コードは質問本文に掲載。

    キャンセル

  • 2017/10/15 00:30

    initializeAppは一番の親であるApp.jsでやるだけでいいと思います。
    SideNavのcomponentWillMountでもやってしまうと、2度初期化することになります。

    userはcomponentWillount関数内で定義された変数なのでその中でしかつかえません。
    これはjs(というかたいていの言語)の基本です。
    stateにuserを入れましょう。

    キャンセル

  • 2017/10/15 00:50

    コンパイル可能になりました。initializeAppはAppに戻しました。stateにuserを追加しました。しかし現在、ログイン状態でも、そうじゃなくても、常に右上がログイン状態です。コンポーネントのリロードが必要なのでしょうか。それとも二項演算子の書き方がまずいでしょうか。

    キャンセル

  • 2017/10/15 01:44

    userはcomponentWillountで取得するので、その時点でログインできていなければnullになります。
    userはちゃんと取得できていますか?

    キャンセル

  • 2017/10/15 01:46

    chromeならreact devtoosという拡張機能がありまして、これでページ上のコンポーネントのstateなどを確認できますので、デバッグに便利です。
    https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja

    キャンセル

  • 2017/10/15 17:42

    ボタン制御できました。gitにあげました!ログインページで消す条件は思いつかないので放置してます。

    キャンセル

  • 2017/10/15 19:09

    だいぶ苦しいですが<Switch>と<Route>を使うと出来ます。
    https://codesandbox.io/s/2vmwym63r

    キャンセル

  • 2017/10/15 21:42

    ログインページでは消せました。ありがとうございます。次はサインアップページつくろうと思います。

    キャンセル

  • 2017/10/16 11:34

    サインアップのページを簡単に追加しました。アドレスの確認を組み込んでないので適当なアドレスでも登録できるのですが、とりあえず放置で。次に投稿型のホームのビューを作りたいです。 Userの入力によってできた記事を一個づつ追加する感じにしたいのですが、どうやったらできるでしょう

    キャンセル

  • 2017/10/16 11:58

    ユーザーがテキストを入力して投稿するとfirebaseのデータベースに追加され、またユーザーごとに過去の投稿が一覧で見れるようにするということでしょうか。

    具体的な設計がよく分かってないので抽象的にしか言えないですが、投稿ボタンを押したら firebase のデータベースに送るのはまあできると思います。あとはデータベースの変更をリッスンして、変更されたら state を適当に更新すればいいと思います。

    firebaseのデータベースの使い方はここを見れば分かるでしょう。
    https://firebase.google.com/docs/database/web/read-and-write?hl=ja

    キャンセル

  • 2017/10/16 12:20

    言い忘れてましたが、onAuthStateChangeのリスナーを設定するcomponentは、unmount(ページから削除される)の時にunsubscribe(リスナーを外す)する必要があると思います。そうしないと、その component を表示するたびにリスナーがどんどん増えてしまいます。

    componentWillUnmount()
    https://reactjs.org/docs/react-component.html#componentwillunmount

    https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
    この返り値が unsubscribe する関数そのものなので、これを state に保存しておいて、componentWillUnmountで呼び出せばよいでしょう。

    componentWillMount() {
    var unsub = firebase.auth().onAuthStateChanged(function(user) {
    // ...
    });
    this.setState({ unsub });
    }

    componentWillUnmount() {
    this.state.unsub();
    }

    キャンセル

  • 2017/10/16 23:41

    そうですね。まず作る段階に入るために欲しい機能は、ログインユーザーが、mypageを作るボタンを押したら、'./createMyPage'に飛ばして。そのページでタイトル、説明欄を入力し、mypage作成を押すと'./UserName/Top'のページを作り、そのユーザーが記入した内容を登録されるっていう機能です。ここで入力されて登録した内容は、時系列表示じゃなくて、固定表示で出したいです。とりあえず、'./createMyPage'を作り、そこへのリンクを作り、mypage作成画面までは自分で作れると思うので、作成していこうと思うのですが、'./UserName/Top'のページを作り、そのユーザーが記入した内容を登録するという機能を考えていたのですが、投稿内容を値としてデーターベースに送ればいいってことまではわかったんですが、その後の昨日作成に詰まりそうなのですが、受け取ったデータをもとに新規作成するという機能はどうやって実現できますか?教えてください。

    キャンセル

  • 2017/10/17 00:55

    受け取ったデータをもとに新規作成するというのは、データベースから受け取ったユーザーデータを元にページを表示するということですか?

    この記事(とPart2も)が参考になると思います。
    https://css-tricks.com/intro-firebase-react/#article-header-id-10
    要はイベントハンドラーを設定して、データベースに変更があったら、state を更新するってことです。

    キャンセル

  • 2017/10/17 14:24

    makemypageを作成し、入力されたデータをデーターベースに記録するところまで作成しました。今後、makeMyPageは1ユーザー1ページのみとしたいのでその処理も書きたかったのですが、とりあえず放置です。ホームにその値を参照し、表示させるという機能も必要なので実装します。


    それと並行して、./userpage/:username(これは本人以外のビュー。コメント機能もおいおい付けたい。)と、./mypage/:username(これは本人のビュー。新規投稿等機能つき。新規投稿はユーザーページに時系列でマイルされるようにしたい。)
    を作りたいのですが、とりあえず、ビューの作成にあたり、そのリンク元の行き先を、<Route path='./mypage/:username'>としたいのですが、どうやってパスにusernameを渡したらよいのかわかりません。あとはビューの内容は、usernameをもとにデーターベースからデータを取ってくるようにすれば良いですよね?

    不明点教えてください。

    キャンセル

  • 2017/10/17 14:25

    makeMyPageも含めたコードはgithubにあげてあります。

    キャンセル

  • 2017/10/17 14:45

    this.props.history.push('/mypage/' + this.state.username)
    とすれば、動的にマイページに飛べると思いますが、
    /mypage/:username ではなく /mypage でもいいのでは?
    ユーザー名は firebase から取得できるので。

    > usernameをもとにデーターベースからデータを取ってくるようにすれば良いですよね?

    firebaseについてはよく知らないのですが、たぶんそうなんじゃないでしょうか。

    キャンセル

  • 2017/10/17 16:08

    質問文更新しました。データーベースの内容を参照するところまではかけたと思うのですが、その後、個別の値をstateに入れて、renderで表示するという一連の流れができません。教えてください

    キャンセル

  • 2017/10/17 16:50

    firebaseについてはよく知らないので、公式ガイドを見ながら書いてますが、まずデータベースをどういう形にするのか決めたほうがよいと思います。

    公式ガイド
    https://firebase.google.com/docs/database/web/structure-data
    なるべく階層が深くならないようにしたほうがいいと書いてあります。

    makeMyPageは、自分のページを作るだけですよね?
    データを取得して表示するのは別のページじゃないでしょうか。

    データを取得する方法は、先程の記事に例が書いてあります。

    itemsRef.on('value', (snapshot) => {
    console.log(snapshot.val());
    });

    .on で ref の value イベントにリスナーを設定すると、.on を実行した直後に1回呼び出され、その後 ref の内容が変化するたびに呼び出されます。
    https://firebase.google.com/docs/reference/js/firebase.database.Reference#on
    これを使って state を データベースにあるユーザーデータと同期させればいいと思います。

    キャンセル

  • 2017/10/17 16:52

    ちなみに ref.on も onAuthStateChanged と同様に、コンポーネントが unmount するときにリスナーを外したほうがいいと思います。ref.off でできます。
    https://firebase.google.com/docs/reference/js/firebase.database.Reference#off

    キャンセル

  • 2017/10/17 21:58

    ありがとうございます。やってみます。ところで、質問文のコードでしたが、中身を別のページからきりはりするときに、コンポーネントの名前を切り取り元のそのままにしてしまったようです。先述のコードも現在のコードも、名前以外はそのままですが、mypageのものです。なので、データを取得し、表示するコードであってます。

    キャンセル

  • 2017/10/20 10:28

    firebase.database().ref.once(this.state.userId).then(function(dataSnapshot) {
    //handle read data
    });
    を使って、データを取得し、表示しようと思います。
    dataSnapshot.val() がデータベースからの出力でしょうか?
    どうやってstateに代入したらよろしいでしょうか。

    キャンセル

  • 2017/10/20 11:01

    const self = this
    firebase.database().ref().once('value').then(function(snapshot) {
    self.setState({
    username:snapshot.val().user,
    explain:snapshot.val().explain,
    title:snapshot.val().title
    })
    });
    };
    これで多分、stateに入れられてます。このあと表示させたいのですが、
    render() {
    return (
    <div>
    <div className='col s12 m4'>
    <Card>
    <CardTitle title='Mypage for {username}' subtitle='{this.state.title}'/>
    <CardText>
    {this.state.explain}
    </CardText>
    </Card>
    </div>
    </div>
    )}
    }
    こうすると変に表示されてしまいます。基礎のような気もしますが、解決案が見つかりませんでした。

    キャンセル

  • 2017/10/20 13:05

    > dataSnapshot.val() がデータベースからの出力でしょうか?

    そうです。どうやってusernameなどを取り出すかは、完全にそのデータベースの形式次第です。
    console.log(snapshot.val())
    をして、どのような形式か確認してみてはどうでしょうか。

    現在ログインしているユーザーのデータだけ取得できればいいんですよね?
    firebase.database().ref().once.... とすると、データベースまるごと取得することになります。
    .ref の引数にパスを指定して、現在のユーザーのデータだけを取得するようにしたほうがいいと思います。

    あと、{username} ではなく {this.state.username} ですよ。

    最後に、snapshot.val() がどれだけ重い処理かわからないですが、何回も呼び出すのは無駄に処理が増えるので、返り値を変数に入れるなどして避けたほうがいいと思います。

    キャンセル

  • 2017/10/22 21:37

    ref()の中身に苦戦していましたが、登録したデーターベースの名前を””で囲んで入れるとうまくいきました。これでユーザー情報の中身のみ取得できます。console.logで確認したところ、

    4VOa8044WNb9Detv59DjyLGjMyz2:
    explain:"adatda"
    title:"adta"
    user:"TTOT"
    と、取得内容がなっていました。
    この場合、この値たちはどうやって参照したらいいのでしょうか、試行錯誤してみますが、アイディアあればお願いします。

    キャンセル

  • 2017/10/22 21:37

    左はデーターベースの種別、右はユーザーが打ち込んだ中身です。

    キャンセル

  • 2017/10/22 21:38

    一番上はユーザーIDです

    キャンセル

  • 2017/10/22 21:42

    おそらくオブジェクトだと思うので普通に
    const record = snapshot.val();
    const explain = record.explain;
    のように取り出せると思います。

    キャンセル

  • 2017/10/22 22:37

    そうやってみたのですが、console.log('%s',record.title)で表示させるとundifinedとなってしまいます。どうやって取得したらいいんでしょうね?引き続きこちらは試行錯誤いたします。

    キャンセル

  • 2017/10/22 23:02

    今気づいたのですが、"users"にすると、データーベースのユーザー全ての情報を参照してしまう状態のようです。改善します。

    キャンセル

  • 2017/10/22 23:22

    もしかすると snapshot.val() はこんな感じですか?
    { 4VOa8044WNb9Detv59DjyLGjMyz2: {
    explain:"adatda",
    title:"adta",
    user:"TTOT"}
    }
    そしてこれが"users"の値ということでしょうか?
    ならば .ref("users/" + <ユーザーID>) みたいにすれば現在のユーザーのデータだけ取り出せるんじゃないでしょうか。
    公式ガイド
    https://firebase.google.com/docs/database/web/read-and-write

    キャンセル

  • 2017/10/22 23:35

    現在あまりに苦戦するため、データーベースの中身の受け取りについてはfirebaseのサポートに連絡して、返事待ちをしております。また、個別のデータの受け取りの部分のコードは現在、そのように改善し、 const myUserId = this.state.userId
    firebase.database().ref('/users/'+myUserId).once('value').then((snapshot) のように個別のデータを受け取れるようにいたしました。中身がundifinedである現状はすぐに改善策が思いつかない場合は放置し、userの投稿が可能になるように投稿のビューを作成しようかなと思います。

    キャンセル

  • 2017/10/22 23:39

    現在のコンソールの表示は、
    4VOa8044WNb9Detv59DjyLGjMyz2:
    explain:"adatda"
    title:"adta"
    user:"TTOT"

    こんな感じです。

    キャンセル

  • 2017/10/22 23:41

    一応、githubにコンソールログを表示できる状態でコミットしたので、僕の口頭でわかりづらければお手数ですが、ご確認を。

    キャンセル

  • 2017/10/23 00:34

    今気づきましたが、componentWillMount の中で this.state.userId を取得しようとしていますが、onAuthStateChangedのイベントハンドラーが呼び出されるのはログイン状態が変わったときだけです。なのでそのやり方では uid は取得できません。

    componentWillMount 内で firebase.auth().currentUser.uid を取得すればいいと思います。

    キャンセル

  • 2017/10/23 13:04

    コード更新しました。willmountで受け取って、didmountで使ってるんですけど、これではダメってことですか?

    キャンセル

  • 2017/10/23 13:32

    そのコードでは userId が set されるのはいつでしょうか?
    componentWillMount より後にログイン状態が変化したときです。普通変わらないですよね。
    なので componentDidMount の時点で userId がセットされている保証は全くありません。

    キャンセル

  • 2017/10/23 16:34

    間違っていました。ご指摘ありがとうございます。入れ子を勘違いしていました・・・ 修正コードをあげています。

    キャンセル

  • 2017/10/23 17:33

    現在、username以外は表示できております。というより、title={this.state.username}はできますが、title='mypage for {this.state.username}'はできません。mypage for {this.state.username}と表記されます。それ以外のバグは修正できました。

    キャンセル

  • 2017/10/23 17:36

    Mypage内にmakepostの機能を追加していきます。

    キャンセル

  • 2017/10/23 17:39 編集

    title={"mypage for " + this.state.username}
    とすればいいです。{ } 内には任意の js expression が書けます。

    キャンセル

  • 2017/10/23 19:06

    git 質問共に更新しました。投稿のビューで不明点があって、0投稿の時はPOSTEDにてyour posting does not exsist.と表示。投稿があるときは最近の投稿5つを降順(最新の投稿順)で表示させたいです。この場合分けと、降順による表示の考え方がいまいちわかりません。
    どうしたら良いでしょうか。

    キャンセル

  • 2017/10/23 19:42

    データベースから取得した投稿データはどんな形式でしょうか。
    render関数内で三項演算子などを用いれば場合分けは簡単です。
    時系列順に並んでいるなら最後の5つを取ればいいですね。
    demo
    https://codesandbox.io/s/yv4wkz2y6z

    キャンセル

  • 2017/10/23 19:53

    .slice(-5) で最後の5つを取り、.reverse() で逆順にしています。

    キャンセル

  • 2017/10/23 21:59

    firebaseによると、
    var newPostKey = firebase.database().ref().child('posts').push().key
    var updates = {};
    updates['/posts/' + newPostKey] = postData;
    updates['/user-posts/' + uid + '/' + newPostKey] = postData;
    return firebase.database().ref().update(updates);
    で、同時に二つの場所にアップデートする関数を作ることができるようなのですが、これはReactの構文的にはまずいですよね?書き直したらどのようになるでしょうか。この関数をPOSTボタンを押すときに呼び出すようにすればいいということでしょうかね?

    キャンセル

  • 2017/10/23 22:17 編集

    データベースの2つの場所に同じデータを入れるのはよくないと思いますが(そういう意味で書いていないならすみません)、どこがReact的にまずいとお思いなのでしょうか?

    > この関数をPOSTボタンを押すときに呼び出すようにすればいいということでしょうかね

    ユーザーがPOSTボタンを押した時にデータベースにプッシュしたいならそれは当然のような気がするのですが、他にどのような選択肢があるのでしょうか?すみません質問の意図がよく分からないです。

    キャンセル

  • 2017/10/23 22:27

    ごめんなさい。いつもと違う形式なので戸惑いを含む質問でした。。質問内容まとめたのでお願いします。
    function writeNewPost(uid, username, title, body) {
    // A post entry.
    var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    };

    // Get a key for a new Post.
    var newPostKey = firebase.database().ref().child('posts').push().key;

    // Write the new post's data simultaneously in the posts list and the user's post list.
    var updates = {};
    updates['/posts/' + newPostKey] = postData;
    updates['/users/' + uid + '/' + newPostKey] = postData;

    return firebase.database().ref().update(updates);
    }

    これが、上書きなしのアップデート関数のようなので、
    firebase.database().ref('users/' + this.state.userId+'/posts/').set
    の代わりにhandleSubmitの中に入れたいのですが、そうした場合、このままhandleSubmitの中に入れていいのか、
    さらにfunctionの引数に入れたい値を代入しておいてこれを呼び出すだけでいいのか、それともpostDataの中をいじればいいのか。わからないです!
    こうじゃないパターンとしては、これを関数として定義しておき、それをhandleSubmitの中で呼び出すのかなと思ったのですが、その場合、この関数の呼び出しはfirebase.database().ref().update(updates)でしょうか? となると引数は定義したところでstateを使って設定しておくんですかね?

    キャンセル

  • 2017/10/23 22:35

    handleSubmit=(event) =>{
    const self = this
    function writeNewPost(self.state.userId, self.state.username, self.state.post_title, self.state.post_body) {
    // A post entry.
    var postData = {
    author: self.state.username,
    uid: self.state.userId,
    body: self.state.post_body,
    title: self.state.post_title,
    starCount: 0,
    };

    // Get a key for a new Post.
    var newPostKey = firebase.database().ref().child('posts').push().key;

    // Write the new post's data simultaneously in the posts list and the user's post list.
    var updates = {};
    updates['/posts/' + newPostKey] = postData;
    updates['/users/' + self.state.userId + '/user_posts/' + newPostKey] = postData;

    return firebase.database().ref().update(updates);
    }
    };
    こうやって書いてみたのですが、「Syntax error: Unexpected token, expected , 」と表示されるので、state入れられないのですかね?

    キャンセル

  • 2017/10/23 22:45

    申し訳ないです。めちゃめちゃいじったらうまくいきました。完成したらコードあげます。

    キャンセル

  • 2017/10/23 23:00

    writeNewPostの関数定義の引数に、実際の値を書いてるせいです。そこは引数名を書く所です。
    writeNewPost は handleSubmit などと並列にメソッドとして定義してはどうでしょうか。

    あと、 postData のキー名が公式ガイドそのままですが、それは自由ですよ。

    私もfirebaseをよく理解してないのでなんですが、投稿を2箇所に保存する必要が本当にあるのか少し気になります。

    キャンセル

  • 2017/10/23 23:15 編集

    了解です。サイトの構造を踏まえつつ、そこの扱いについては今後考えることにします。アドバイスありがとうございます。
    現在、karamarimoさんがくれたデモを元に、ポストが0の時、No entryとするために書いてみたのですが、
    <Tab label="POSTED" value="b">
    <div>
    {
    this.state.posts.length === 0
    ? <div><h>No entries</h></div>
    : <div className='col s12 m4'>
    <Card>
    <CardTitle title={this.state.post_title} subtitle={this.state.username} />
    <CardText>
    {this.state.post_body}
    </CardText>
    </Card>
    </div>
    }
    </div>
    </Tab>

    こうすると、 this.state.posts.length === 0
    の部分のlengthがnullだと言われるのですが、初期値の与え方が悪いのでしょうか、それとも、別の条件で分岐しないとまずいんですかね?(ポストを複数しているユーザーと0のユーザーそれぞれでテストしましたが、0のユーザーだけの症状です。)
    それと、今後の直近の過去ポスト5つを表示する機能について、確認したいのですが、今回のデモの場合、 this.state.items.slice(-5).reverse().map(
    item => <li>{item.name}</li>、としていますが、これをmaterial ui <card>に載せる場合、どうしたら良いのでしょうか?

    キャンセル

  • 2017/10/23 23:46

    > this.state.posts.length === 0の部分のlengthがnullだと言われるのですが

    そもそも this.state.posts をセットしているコードが見当たらないですが...。

    > これをmaterial ui <card>に載せる場合、どうしたら良いのでしょうか?

    <li>{item.name}</li> を次のようにすればいいと思います。
    <Card>
    <CardTitle title={item.post_title} subtitle={item.username} />
    <CardText>
    {item.post_body}
    </CardText>
    </Card>

    キャンセル

  • 2017/10/24 13:49

    ありがとうございます。コードの更新しておらず、postsが抜けておりました。修正コードをあげました。

    デモのコードで不明点があるのですが、
    this.state.items.slice(-5).reverse().map(
    item => <li>{item.name}</li>

    これのitemってどこで出てきたitemなのかということと、item => の機能がよくわかっていません。教えていただけないでしょうか。

    キャンセル

  • 2017/10/24 13:53

    現在、新しいコードに修正しています。 自分のコードでいうpost(デモでいうitem)がundefinedとなるためこのような質問をしました!

    キャンセル

  • 2017/10/24 14:20 編集

    item => <li>{item.name}</li>
    は arrow function といって、この場合
    function (item) { return <li>{item.name}</li> }
    と同じです。配列のmap関数は分かりますか?ご存じないなら調べてみてください。

    おそらく self.setState({ posts: snapshot.val() }); でセットした値が配列じゃないからだと思います。
    firebaseでは配列ではなくランダムに生成されたキーでリストを管理するみたいですね。
    一度↓こんな感じで snapshot.val() の中身を出力してここにコピペしてもらえますか?
    console.log(JSON.stringify(snapshot.val()));

    キャンセル

  • 2017/10/24 14:37

    map関数を勉強しておきます。

    {"-Kx8TSa9k0axbP2tWuY2":{"author":"MOTO","body":"Guys","starCount":0,"title":"Hey","uid":"tkI5FZ6gizduCcdsIUwY1iObxUX2"},"-Kx8VphkWvgdIR0bplWn":{"author":"MOTO","body":"2","starCount":0,"title":"2","uid":"tkI5FZ6gizduCcdsIUwY1iObxUX2"},"-Kx8VvXibSfWiGPZb23u":{"author":"MOTO","body":"3","starCount":0,"title":"3","uid":"tkI5FZ6gizduCcdsIUwY1iObxUX2"},"-Kx8WMb_ysQWdxOIjC_N":{"author":"MOTO","body":"","starCount":0,"title":"4","uid":"tkI5FZ6gizduCcdsIUwY1iObxUX2"}}

    console.log(JSON.stringify(snapshot.val()));やってみました。これで大丈夫ですかね?

    キャンセル

  • 2017/10/24 16:56

    ありがとうございます。
    やはり直接 snapshot.val() でオブジェクトにしてしまうと時間順に投稿を取り出すのが面倒ですね。
    調べてみると forEach メソッドを使えばそれができるようです。
    https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach
    これを使って投稿の配列を作り state に入れればいいのではないでしょうか。

    const posts = [];
    snapshot.forEach(childSnapshot => posts.push(childSnapshot.val());
    this.setState({ posts: posts });

    キャンセル

  • 2017/10/24 21:27

    <div>
    <span>{ this.state.posts.length }</span>
    {
    this.state.posts.length === 0
    ? <div><h>There is no post</h></div>
    : <ul>{
    this.state.items.slice(-5).reverse().map(
    function(post){
    <div className='col s12 m4'>
    <Card>
    <CardTitle title={post.post_title} subtitle={this.state.username} />
    <CardText>
    {post.post_body}
    </CardText>
    </Card>
    </div>
    }
    )
    }</ul>
    }
    </div>

    現在これがうまくいきません。Unhandled Rejection (TypeError): Cannot read property 'slice' of undefined だそうですが、よくわからんエラーです。

    const posts = [];
    snapshot.forEach(childSnapshot => posts.push(childSnapshot.val());
    this.setState({ posts: posts });

    この件は firebase.database().ref('/users/'+myUserId+'/user_posts/').once('value').then(function(snapshot) {
    snapshot.forEach(function(childSnapshot){
    const posts = [];
    posts.push(childSnapshot.val()),
    self.setState({ posts: posts });
    })
    });
    }
    このように導入して、うまく作動してるのは確認できました。ありがとうございます。

    キャンセル

  • 2017/10/24 21:28

    ちなみに、ポストが0のアカウントの動作は可能になりました。ありがとうございます。
    今回のエラーは1つ以上ポストのあるアカウントです。

    キャンセル

  • 2017/10/24 21:42

    this.state.items.slice(-5) ではなく this.state.posts.slice(-5) ですね。

    キャンセル

  • 2017/10/24 21:58

    ありがとうございます。ケアレスミスです。コンパイルは通ったのですが、カードが表示されません。
    また、4ポストあるはずですが、<span>{ this.state.posts.length }</span>が1とだけ表示されるだけです。どうしたら良いでしょう。

    キャンセル

  • 2017/10/24 22:07

    snapshot.forEach(function(childSnapshot){
    const posts = [];
    posts.push(childSnapshot.val()),
    self.setState({ posts: posts });
    })
    ではなく
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val());
    })
    self.setState({ posts: posts });
    ですね...。なぜだか分かりますか?

    また
    <CardTitle title={post.post_title} subtitle={post.username} />
    <CardText> {post.post_body}
    と書いていますが、post は先程貼っていただいたように
    {"author":"MOTO","body":"Guys","starCount":0,"title":"Hey","uid":"tkI5FZ6gizduCcdsIUwY1iObxUX2"}
    という形なので、post_title などのプロパティは存在しませんよ。

    キャンセル

  • 2017/10/24 22:16

    上記だと。ファンクション後に消失してしまう。ですか?自信ありませんが。

    stateやらデータベースやらをいじりすぎてこんがらかってしまいますね。ご指摘ありがとうございます。助かりました。
    そのようにコード修正を本文に貼らせていただきます。


    さて。直してコンパイルして動作を見てみたのですが、posts.lengthは4となっていて正しいのですが、
    カードがやはりどうしても表示されません。
    どこがおかしいのでしょうか。
    動作確認可能なようにgithubも更新しておきますね。

    キャンセル

  • 2017/10/24 22:57

    > ファンクション後に消失してしまう。ですか?
    まぁそういうことです。ループのたびに posts が [] に戻ってしまいます。

    >カードがやはりどうしても表示されません。
    map(function(){ ... }) 内で何も return していないからです。
    this.state.posts.slice(-5).reverse().map(
    function(post){
    return (
    <div className='col s12 m4'>
    <Card>
    <CardTitle title={post.title} subtitle={post.author} />
    <CardText>
    {post.body}
    </CardText>
    </Card>
    </div>
    )})

    キャンセル

  • 2017/10/24 23:06

    表示できました!!!!ありがとうございます!!!!

    キャンセル

  • 2017/10/24 23:10

    ホームのビューも同じようにいじってみます。

    ところで、mypageにて新しい投稿をしたら、同じページにリダイレクト(日本語あってるかな?ページ更新)して、すぐ新しい投稿を表示できるようにしたいのですが、その場合は何をしたら良いでしょう。

    キャンセル

  • 2017/10/24 23:19

    表示を更新するために同じページに飛ぶというのはぎこちないですね。
    今、componentDidMount にて".once"で一度だけデータをリクエストして取得していますよね。
    それを".on"にすれば、いつでも値が変わった時に取得できますね。

    キャンセル

  • 2017/10/24 23:39

    var onValueChange = firebase.database().ref('/users/'+myUserId+'/user_posts/').on('value',function(snapshot) {
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val()),
    self.setState({ posts: posts });
    })
    });
    this.setState({off:onValueChange})
    ↑---------------------didMount

    componentWillUnmount() {
    const self = this
    this.state.unsub();
    firebase.database().ref.off('value', self.state.off)
    };


    としたのですが、
    TypeError: __WEBPACK_IMPORTED_MODULE_5_firebase___default.a.database(...).ref.off is not a function

    となります。https://firebase.google.com/docs/reference/js/firebase.database.Reference#off
    を参照したのですが、何が間違っているのでしょう。。

    キャンセル

  • 2017/10/25 00:00

    むむ。色々トラブルケーシングをしてたところ、makeMyPageに行こうとする時のみのバグっぽいので大丈夫かな?Home、Settingには飛べるので、大丈夫そうです。今後、直すべきバグなのかな?とりあえず機能には問題なさそうです!

    キャンセル

  • 2017/10/25 00:01

    いや、ダメな時はhomeもダメですね。とりあえず自分はhomeのビューを作ります。

    キャンセル

  • 2017/10/25 00:06

    firebase.database().ref.off('value', self.state.off)
    ref のあとに () が抜けてます。ただ".on"した所と同じrefに対して".off"しないといけないと思うので、
    firebase.database().ref('/users/'+this.state.userId+'/user_posts/').off('value', self.state.off)
    ですね。

    キャンセル

  • 2017/10/25 00:43

    ありがとうございます。修正できました!

    現在、ホームのビューを作成しています。今後ホームに実装する機能として、
    ①スターをつけたユーザーのカードを表示し、そこには最新のユーザーの記事を載せていきます。
    ②ユーザーの検索機能。 これがないと①の機能を使いづらいのでなんとかしたいです。
    ③記事ごとのコメント機能  これはぜひほしい機能ですが、1、2に比べると重要性が薄く、まだまだ先でいいです。

    という状況です。下のコメントにそれぞれの進行状況と、困りそうなことを列挙します。

    キャンセル

  • 2017/10/25 00:52

    ①とりあえず大枠はできました。データ管理で苦心しています。とりあえず、スターした時の機能として、ユーザーのデータの子に新たに/stared_users/を作り、その中に、スターをつけたユーザーIDを保持させればいいと思います。
    そして、そのIDを参照して、最新の投稿のみを取ってくればいいと思うのですが、複雑になってきており混乱しています。
    componentDidMount(){
    const myUserId = this.state.userId
    const self = this
    var onValueChange = firebase.database().ref('/users/'+myUserId+'/stared_users/').on('value',function(snapshot) {
    const stared_users = [];
    snapshot.forEach(function(childSnapshot){
    stared_users.push(childSnapshot.val()),
    self.setState({ stared_users: stared_users });
    })
    });
    このようにし、stateの stared_usersが0の場合はThere is no stared userとするところまではできています。
    スター機能をつけ、スターしたユーザーを参照できるようになったら、stateのpostsに(これはユーザーの最新投稿を保存)に
    var onValueChange = firebase.database().ref('/users/'+stared_UserId+'/user_posts/').on('value',function(snapshot) {
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val()),
    self.setState({ posts: posts });
    })
    });
    こうしていけば良いのでしょうが、stared_UserIdをどのように制御したら良いのかわからず、ここで手詰まりそうだなと思っています。教えていただけると助かります。それと、これでは全てのポストを受け取ってしまうような気がするのですが、最新の投稿を取るにはどうしたらいいか。も知りたいです。

    キャンセル

  • 2017/10/25 00:54

    ②このwebサイトに検索エンジンを導入するにあたり、どのようなフレームワークやライブラリ、もしくは外部のアプリ等、がオススメでしょうか?初期搭載としては、あまりこだわらなくていいので、簡単に導入できるものがいいです。オススメを教えてください。

    キャンセル

  • 2017/10/25 00:56

    ③これはpostsの子としてcommentsを入れてその中に、コメントたちを記事ごとに管理して表示していけばいいかもしれませんね。文章書いてたらなんとなく考えがまとまってきました。しかし、レイアウト的に考えて、カードじゃコメントと投稿の区別がわからなくなってしまうので、その辺をうまくレイアウトする方法が難しいなと思います。簡単な方法あればなと思います。

    キャンセル

  • 2017/10/25 00:57

    いっぺんにたくさんの質問をしてしまい申し訳ありません。少しずつ、可能な時に回答いただければ嬉しく思います。

    キャンセル

  • 2017/10/25 11:49

    > これでは全てのポストを受け取ってしまうような気がするのですが
    そうですね。最新のだけ取ってくるほうがいいですね。
    .slice(-5) で最新の5つを表示するようにはしていましたが、それ以外表示しないなら無駄ですね。

    > 最新の投稿を取るにはどうしたらいいか
    https://firebase.google.com/docs/database/web/lists-of-data#filtering_data
    ここを見ると limitToLast() というメソッドがあるのでこれを使えばいいのではないでしょうか。

    > このwebサイトに検索エンジンを導入するにあたり、どのようなフレームワークやライブラリ、もしくは外部のアプリ等、がオススメでしょうか?
    結局データはFirebaseにあるので、Firebaseの検索機能を使うか、外部にインデックスを作るしか無いと思います。
    しかしFirebaseのRealtime DBには部分文字列検索の機能がありません。
    完全一致検索や先頭文字列検索(例えば"Firebase"が"Fire"でヒット)ならできます。
    部分文字列検索は必要でしょうか?

    キャンセル

  • 2017/10/25 12:51

    ざっと調べたところ、firebaseの検索機能を見つけられません。お手数ですが、urlをいただけると幸いです。

    最新の投稿は試行錯誤してみます。

    キャンセル

  • 2017/10/25 12:54

    それと、軽微なバグなのですが、HomeとMyPageにいるときに、ブラウザのリロードをすると、
    TypeError: Cannot read property 'uid' of null
    となるのですが、unsubの導入箇所が悪いのでしょうか。componentWillMount内に現在あります。

    キャンセル

  • 2017/10/25 13:16

    > firebaseの検索機能を見つけられません
    明示的に検索機能とは書いてないですが、先程の limitToLast() と同じところに startAt() と endAt() があり、これを使うと先頭文字列による検索ができます。
    参考
    https://stackoverflow.com/questions/38618953/how-to-do-a-simple-search-in-string-in-firebase-database

    > TypeError: Cannot read property 'uid' of null
    エラーとともにソースコードの該当箇所か行番号が表示されると思うのですが、それも教えていただけますか?

    キャンセル

  • 2017/10/25 14:24

    > カードじゃコメントと投稿の区別がわからなくなってしまうので、その辺をうまくレイアウトする方法が難しいなと思います。
    1つのカードに投稿とコメントをまとめてもいいんじゃないでしょうか。こんな感じで。
    https://codesandbox.io/s/7w8mv8oz0q

    キャンセル

  • 2017/10/25 16:44

    > 31 | userId : firebase.auth().currentUser.uid

    ここがエラーになります。


    検索機能の件了解です。導入検討してみます。
    カードのレイアウトの件も、この感じで導入してみます。

    キャンセル

  • 2017/10/25 16:50

    リロードすると認証が外れるからでしょうか...。
    他のページではリロード時にログイン状態は維持されますか?

    キャンセル

  • 2017/10/25 17:34

    ええと、home MyPageはリロードすると表示できなくなり、さっきの警告です。
    そうなった状態で、loginとsignupのURLを直にうつと表示できます。

    そもそも、login signupは認証されていたら入れないページです。
    なんでエラーなのかはよくわからないです。

    キャンセル

  • 2017/10/25 17:55

    Home が表示できない理由はわからないですが、MyPageについては次のような理由ではないでしょうか。

    リロード直後はApp.js で initializeApp が実行されますが、認証が完了する前に MyPage の componentWillMount() が実行され、currentUesr が null になってしまうからだと思われます。この場合は、認証が完了すると onAuthStateChanged のコールバックが呼び出されるので、その中で uid と投稿を取得しましょう。

    なので、まず componentDidMount() の中身を取り出して1つの関数にしましょう(仮に fetchPosts とする)。
    componentWillMount() 内で currentUesr がもし null でなければ this.state.userId を更新し「その後に」 fetchPosts を実行します。また onAuthStateChanged のコールバックで、user が null でない ならば this.state.userId を更新し、「その後に」fetchPosts を実行します。

    注意すべき点は setState() は非同期です。
    https://reactjs.org/docs/react-component.html#setstate
    第2引数に、state が更新された時に呼び出されるコールバックを指定できるので、これに fetchPosts を指定するといいと思います。

    キャンセル

  • 2017/10/25 18:50

    アドバイスを参考に、
    fetchPosts(){
    const myUserId = this.state.userId
    const self = this
    firebase.database().ref('/users/'+myUserId).once('value').then(function(snapshot) {
    self.setState({
    username: snapshot.val().user,
    title: snapshot.val().title,
    explain: snapshot.val().explain
    });
    });
    var onValueChange = firebase.database().ref('/users/'+myUserId+'/user_posts/').on('value',function(snapshot) {
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val())
    self.setState({ posts: posts });
    })
    });
    this.setState({off:onValueChange})
    }


    componentWillMount(){
    const self=this
    var unsub = firebase.auth().onAuthStateChanged(function(user){
    if(user){
    self.setState({
    userId : firebase.auth().currentUser.uid
    });
    fetchPosts()
    }
    else {
    self.props.history.push('/login')
    }
    })
    this.setState({unsub});
    };

    componentDidMount(){

    }

    としたのですがwillmount内のfetchPostsが'fetchPosts' is not defined no-undef
    となります。どこがミスってるのでしょうか。

    キャンセル

  • 2017/10/25 19:39

    メソッドなので this.fetchPosts としなければいけません。
    また前コメントの通り this.setState のコールバックに指定しないと上手くいかないと思います。

    キャンセル

  • 2017/10/26 06:57

    mypageのuidの件、直せました。ありがとうございます。

    現在検索のコードを作成中なのですが、検索の入力、検索をするボタンを
    <Card>
    <CardText>
    <TextField
    id="search"
    floatingLabelText="type username for search"
    value={this.state.search}
    onChange={this.handleChange_search}
    />
    <RaisedButton
    label="Search"
    secondary={true}
    onClick={this.handleSearch}
    />
    </CardText>
    </Card>

    ハンドルサーチを
    handleSearch = (event) =>{
    const self = this
    firebase.database().ref().startAt(self.state.search).endAt(self.state.search+"\uf8ff").once("value")

    this.setState({searchReference:'1'});
    };
    このようにし、searchReferenceが0の時は何も表示しない状態ですが、検索し1になると、検索結果のカードを出力するように作成したつもりなのですが、

    <div>
    {
    this.state.searchReference === 0
    ? <div><h>There is no such user</h></div>
    :<ul>{
    this.state.posts.slice().reverse().map(
    function(stared_user){
    return(
    <div className='col s12 m4'>
    <Card>
    <CardTitle title={stared_user.title} subtitle={stared_user.author+ " star:"+stared_user.starCount} />
    <CardText>
    a
    {stared_user.body}
    </CardText>
    </Card>
    </div>
    )
    }
    )
    }</ul>
    }
    </div>

    この部分が、0から1になったときにカードが出てきません。カードの内容はデバック用なので適当で、カードテキストのaが表示できればいいかなといった状態なので、詳細は気にしないで欲しいのですが、

    なぜ検索ボタンが押されたときに、条件式の処理が行われないのでしょうか?
    また、この治しかたを教えてください。

    キャンセル

  • 2017/10/26 06:59

    コードの全文を、わかりづらいと思うので、質問文に更新します。

    あと、検索のリファレンスを
    firebase.database().ref().startAt(self.state.search).endAt(self.state.search+"\uf8ff").once("value")

    としたのですが、コードはまだ未完でしょうか?いまいちコードの処理が分かっていません。

    キャンセル

  • 2017/10/26 10:50

    > コードはまだ未完でしょうか?
    "value"イベントのリスナーを渡していないので、意味がないですね...。また ref() (ルート)で検索しているのでうまくいかないでしょう。
    あと this.posts がいろんなところで使われているのと、fetchPosts メソッドが見当たらないですが大丈夫でしょうか?

    キャンセル

  • 2017/10/26 12:19

    ごめんなさい。現在、searchReferenceによる、renderの制御を優先的にやってまして、this posts fetchPostsは考え中で、コピーのまま、置いてあったりします。わかりづらい状況での質問で申し訳ないです。

    それで、searchReferenceによる、検索ボタンを押したときにrenderに検索結果をカードで表示する機能ですが、ボタンを押しても表示されません。ライフサイクルの問題でしょうか?
    ユーザーの入力によって、searchReferenceを1や0にすることで、renderの条件分岐をすることでは、表示を変えることはできないのでしょうか?コーディングミスでしょうか?

    該当のコードは、自分のこのコメントの3つ前に抽出して書いております。

    キャンセル

  • 2017/10/26 13:33

    まず this.state.searchReference は '0' か '1' にセットされるので "=== 0" と比較するのではなく '0' ですね。

    キャンセル

  • 2017/10/26 13:46

    検索結果に仮に posts を表示しようとされているのだと思いますが、posts を取得するコードがないので何も表示されていないのだと思います。適当に初期値を設定されてはどうでしょうか?

    キャンセル

  • 2017/10/26 17:28

    ありがとうございます。
    初期値も変えて、this.state.searchReferenceによる制御が可能になりました。
    fetchPosts メソッドの作成に入りたいのですが、

    fetchPosts(){
    const self = this
    const myUserId = self.state.userId
    const staredUser=self.state.stared_users
    var onValueChange1 = firebase.database().ref('/users/'+myUserId+'/stared_users').on('value',function(snapshot) {
    const stared_users = [];
    snapshot.forEach(function(childSnapshot){
    stared_users.push(childSnapshot.val())
    self.setState({ stared_users: stared_users });
    })
    });
    this.setState({off_stared_user:onValueChange1})
    var onValueChange2 = firebase.database().ref('/users/'+staredUser+'/user_posts/').on('value',function(snapshot) {
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val())
    self.setState({ posts: posts });
    })
    });
    this.setState({off_stared_user_posts:onValueChange2})
    }

    このような感じで、onValueChange1のときに、スターをつけたユーザーのIDを取ってきて、stateの stared_usersに配列で保存するところまではいい感じだと思うのですが、

    onValueChange2のstateの stared_usersの数だけ、繰り返しの処理を行い、最新の投稿を取ってくる。というのが少し困っています。

    this.state.stared_users.lengthで数を取ってこれるのか。
    繰り返しの処理のさせかたはどうやるのか。

    わかりません。教えてください。

    キャンセル

  • 2017/10/26 17:28

    現在のコードを更新しました

    キャンセル

  • 2017/10/26 17:59

    とりあえず setState は forEach の外に出したほうがいいですね。配列に追加するたびに state を更新する必要性がありません。

    キャンセル

  • 2017/10/26 18:29

    はい。出しました。質問文のコード更新します。

    キャンセル

  • 2017/10/26 18:41

    onValueChange2にとりあえずlimitToFirst(1)を追加しました。

    キャンセル

  • 2017/10/26 18:45

    limitToLast(1)と迷いましたが、とりあえず動かしてみてからですね。

    キャンセル

  • 2017/10/26 19:44

    fetchPosts はcomponentWillMountで実行されますが
    const staredUser=self.state.stared_users
    だと staredUser == [] になってしまいますね。

    キャンセル

  • 2017/10/26 19:53

    繰り返しは普通に for 文でやればいいと思いますが、ご存じないのでしょうか?

    for (var i = 0; i < ###.length; i++) {
    // ###[i] を使う
    }

    "/stared_users" が変化したら、その各ユーザーについて .on ではなく .once で一度だけ投稿を取得するのが楽だと思いますよ。もちろんリアルタイムに更新されなくなりますが。

    もし .on でリアルタイムに取得するならイベントリスナーの unsubscribe 関数をそのユーザー数だけ state に保存する必要がありますね。また、"/stared_users" が変化したらその度に全部 unsubscribe する必要があります。

    キャンセル

  • 2017/10/26 20:22

    なるほど!C言語とかのように繰り返しできるんですね。stateの配列も普通の配列と同じ扱いできるんですね。こんな感じでコーディングしてみました。

    fetchStaredUser(){
    const self = this
    const myUserId = self.state.userId
    var onValueChange1 = firebase.database().ref('/users/'+myUserId+'/stared_users').on('value',function(snapshot) {
    const stared_users = [];
    snapshot.forEach(function(childSnapshot){
    stared_users.push(childSnapshot.val())
    })
    self.setState({ stared_users: stared_users });
    });
    this.setState({off_stared_user:onValueChange1})
    }

    fetchStaredUserPosts(){
    const self = this
    for(var i = 0 ; i < self.state.stared_users.length ; i++){
    const staredUser = this.state.stared_users[i]
    var onValueChange2 = firebase.database().ref('/users/' + staredUser + '/user_posts/').limitToFirst(1).on('value',function(snapshot) {
    const posts = [];
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val())
    })
    self.setState({ posts: posts });
    });
    this.setState({off_stared_user_posts:onValueChange2})
    }
    }

    componentWillMount(){
    const self=this
    var unsub = firebase.auth().onAuthStateChanged(function(user){
    if(user){
    self.setState({
    userId : firebase.auth().currentUser.uid
    },self.fetchStaredUser);
    self.fetchStaredUserPosts
    }
    else {
    self.props.history.push('/login')
    }
    })
    this.setState({unsub});
    }

    スターしたユーザーのIDを取ってくるのと、表示するのを分け、componentWillMountで順番に呼び出すようにしました。

    スター機能がまだないので、デバックはできていないのですが、この部分はいい感じな気がします。

    どこか行けないところがあればお願いします。質問文のコード、更新しますね。

    キャンセル

  • 2017/10/26 21:01

    現在、名前検索機能を追加しているんですが、データベース上の管理方法が users/(userId)/user という感じで、名前が各ユーザーIDの子となっていて、どうやってrefしていいかわかりません。
    firebase.database().ref('/users/'+allUserId+'/user/')startAt(self.state.search).endAt(self.state.search+"\uf8ff").once("value").then(function(snapshot)

    ここのallUserIdをどうやって行えばいいんですかね?


    また、ユーザーの名前を参照したあと、そのユーザーのIDを返さなければ行けないんですが、これもまた難しいなと思いました。

    どうしたらいいのでしょうか。

    キャンセル

  • 2017/10/26 23:01

    今、stared usersのビューを作っているのですが、ライフサイクルメソッドの順番を考えて、
    下のコードのようにユーザーIDを取得し、その後にユーザーのポストを表示させようとしてるのですが、
    console.logを使って確認すると、ユーザーのポストのコンソールのHelloが、ユーザーのIDのsnapshotの結果よりもかなり先に出てしまうのですが、どうやって改善できるでしょうか。

    /*------------refer stared user-------------------------------------------------------------------*/

    fetchStaredUser(){
    const self = this
    const myUserId = self.state.userId
    var onValueChange1 = firebase.database().ref('/users/'+myUserId+'/stared_users').on('value',function(snapshot) {
    const stared_users = [];
    snapshot.forEach(function(childSnapshot){
    stared_users.push(childSnapshot.val())
    console.log(childSnapshot.val())
    })
    self.setState({ stared_users: stared_users });
    });
    this.setState({off_stared_user:onValueChange1})
    }

    fetchStaredUserPosts(){
    const self = this
    console.log("Hello")
    for(var i = 0 ; i < self.state.stared_users.length ; i++){
    const staredUser = this.state.stared_users[i]
    console.log(staredUser)
    var onValueChange2 = firebase.database().ref('/users/' + staredUser + '/user_posts/').limitToFirst(1).on('value',function(snapshot) {
    const posts = [];
    console.log(snapshot.val())
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val())
    })
    self.setState({ posts: posts });
    });
    this.setState({off_stared_user_posts:onValueChange2})
    }
    }
    /*------------life cicle method--------------------------------------------------------------------*/

    componentWillMount(){
    const self=this
    var unsub = firebase.auth().onAuthStateChanged(function(user){
    if(user){
    self.setState({
    userId : firebase.auth().currentUser.uid
    },self.fetchStaredUser);
    }
    else {
    self.props.history.push('/login')
    }
    })
    this.setState({unsub});
    }

    componentDidMount(){
    const self=this
    this.setState({},self.fetchStaredUserPosts);
    }

    キャンセル

  • 2017/10/27 00:31

    > users/(userId)/user という感じで、名前が各ユーザーIDの子となっていて、どうやってrefしていいかわかりません。
    user というパスはなく userId の直下に author とか body とかあったと思うんですが...。
    あるキーの値についてソートするには .orderByChild(path) を使います。
    https://firebase.google.com/docs/reference/node/firebase.database.Reference#orderByChild
    ユーザー名は author ですか?(英語として間違っていると思いますが) こんな感じですね。

    firebase.database().ref('/users/').orderByChild('author').startAt(self.state.search).endAt(self.state.search+"\uf8ff")

    キャンセル

  • 2017/10/27 01:28

    > ユーザーのポストのコンソールのHelloが、ユーザーのIDのsnapshotの結果よりもかなり先に出てしまうのですが、どうやって改善できるでしょうか。

    fetchStaredUser も fetchStaredUserPosts も非同期に取得するので、ちゃんとしないとどちらが先に取得されるかは保証されません。
    そもそも componentWillMount と componentDidMount に分ける意味があまりないと思います。componentWillMount で実行した非同期処理は componentDidMount までに終了する保証はどこにもありません。

    また for loop の中で
    self.setState({ posts: posts })
    this.setState({off_stared_user_posts:onValueChange2})
    を実行しているので最後のユーザーのだけ残りますよ。

    ちなみにどうでもいいですが stared ではなく starred ですよ。

    キャンセル

  • 2017/10/27 11:24

    Promiseで順序を取ろうとコードを組んだのですが、
    const stateStarredUsers = this.state.starred_users
    の部分がUnhandled Rejection (TypeError): Cannot read property 'state' of undefined
    となってしまいます。
    Promiseの記事を読んで書いて見ましたが、そもそも使い方を間違っているかもしれません。
    どうしたらいいでしょうか。

    /*------------refer starred user-------------------------------------------------------------------*/

    fetchStarredUser(){
    const self1 = this
    const myUserId = self1.state.userId
    var onValueChange1 = firebase.database().ref('/users/'+myUserId+'/starred_users').on('value',function(snapshot) {
    const starred_users = [];
    snapshot.forEach(function(childSnapshot){
    starred_users.push(childSnapshot.val())
    console.log(childSnapshot.val())
    })
    self1.setState({ starred_users: starred_users });
    });
    this.setState({off_starred_user:onValueChange1})
    }

    fetchStarredUserPosts(){
    const self2 = this
    const posts = [];
    const stateStarredUsers = this.state.starred_users
    console.log("Hello")
    for(var i = 0 ; i < stateStarredUsers.length ; i++){
    const starredUser = self2.state.starred_users[i]
    console.log(starredUser)
    var onValueChange2 = firebase.database().ref('/users/' + starredUser + '/user_posts/').limitToFirst(1).on('value',function(snapshot) {
    console.log(snapshot.val())
    snapshot.forEach(function(childSnapshot){
    posts.push(childSnapshot.val())
    })
    });
    }
    self2.setState({ posts: posts });
    self2.setState({off_starred_user_posts:onValueChange2})
    }

    fetchStarAll(){
    const self = this
    var p1 = new Promise(
    function (resolve, reject) {
    resolve(
    self.fetchStarredUser
    )}
    )
    p1.then(
    self.fetchStarredUserPosts
    )
    }

    キャンセル

  • 2017/10/27 11:25

    検索は未だに結果がnull以外でない状態です。

    キャンセル

  • 2017/10/27 11:26

    結果というより、
    firebase.database().ref('/users/').orderByChild('author').startAt(self.state.search).endAt(self.state.search+"\uf8ff").once("value").then(function(snapshot) {
    console.log(snapshot.val())

    このスナップショットがnullです。

    キャンセル

  • 2017/10/27 12:19

    Promise の使い方が間違ってますね...。
    Promise を使わないとするとこうするでしょうか。
    https://pastebin.com/81bBRbV5
    先程述べた理由で投稿を取得するところは .on ではなく .once を使っています。
    また、各ユーザーについて非同期に投稿を取得しているため、取得するたびに posts を setState せざるを得なくなっています。

    この点 Promise は Promise.all で合流できるので毎回 setState しなくて済みますね。
    しかしPromise でやるなら .on とは相性が悪いです。複数回発生するイベントに Promise は使えません。
    いっそ全部 .once にしてはどうですか?リアルタイム性は失われますが。

    キャンセル

  • 2017/10/27 12:23

    > 検索は未だに結果がnull以外でない状態です。
    検索クエリは大文字と小文字が区別されますが、それに気をつけてもダメですか?

    キャンセル

  • 2017/10/27 12:31

    >検索クエリは大文字と小文字が区別されますが、それに気をつけてもダメですか?

    関係なくダメです。どうしてでしょうか。

    >いっそ全部 .once にしてはどうですか?リアルタイム性は失われますが。

    一度そうしてみます。

    キャンセル

  • 2017/10/27 12:33

    >一度そうしてみます。

    返事を急ぎすぎました。https://pastebin.com/81bBRbV5を参考に行って見ます。

    キャンセル

  • 2017/10/27 12:40 編集

    やっと気づきました。
    firebase.database().ref('/users/').orderByChild('author').startAt(self.state.search).endAt(self.state.search+"\uf8ff").once("value").then(function(snapshot) { ... });
    で then は使えません。
    EDIT: すみません間違えました。使えます。

    キャンセル

  • 2017/10/27 12:58

    勘違いしていましたが、"author"じゃなくて"user"でしたね。

    キャンセル

  • 2017/10/27 13:51

    調べてたら、componentwillmount ではリスナーの登録とかしないほうがいいみたいです。
    https://reactjs.org/docs/react-component.html#componentwillmount
    componentDidMount に移しといてください(他の component も)。

    キャンセル

  • 2017/10/27 21:02

    ありがとうございます。全てdIdmountに入れました! 検索機能は、orderBy(user)で参照し、ユーザー直下にユーザーIDの項目を追加し、実現しました。表示のビューを作成中です。


    コメント機能を作成してたのですが、ポストごとにコメントの子を追加しなければならず、そこの管理をどうしようか困っています。どうしたら良いでしょうか。

    キャンセル

  • 2017/10/27 21:15

    まさにここに似たような例が書いてありますので参考にしてはどうですか。
    https://firebase.google.com/docs/database/web/structure-data#flatten_data_structures

    キャンセル

  • 2017/10/27 23:15

    ありがとうございます。並行して、スター機能を作成していたのですが、

    /*------------add star user------------------------------------------------------------------------*/

    starBtnClick = (value) =>{
    const self = this
    this.setState({willStarUser:value},this.allAddFunction)
    }

    addStarUserToDataBase() {
    const myUserId = this.state.userId
    var starUserData = this.state.willStarUser;
    var newStarUserKey = firebase.database().ref('/users/' + myUserId + '/starred_users/').push().key;
    var updates = {};
    updates['/users/' + myUserId + '/starred_users/' + newStarUserKey] = starUserData;
    return firebase.database().ref().update(updates);
    }

    resetWillStarUser(){
    this.setState({
    willStarUser:''
    });
    }

    allAddFunction(){
    const self = this
    this.addStarUserToDataBase(function() {
    self.resetWillStarUser()
    })
    }


    このようにし。

    <div>
    {
    this.state.searchReference === '0'
    ? <div><h>here is search reference view</h></div>
    :<ul>{
    this.state.search_users.slice().map(
    function(searchRef){
    const self = this
    return(

    <div className='col s12 m4'>
    <Card>
    <CardTitle title={searchRef.user} subtitle={searchRef.title} />
    <CardText>
    {searchRef.explain}
    </CardText>
    <CardActions>
    <FlatButton primary label="STAR" onClick={this.starBtnClick} value={searchRef.uid} />
    </CardActions>
    </Card>
    </div>
    )
    }
    )
    }</ul>
    }
    </div>

    こうしたのですが、
    <FlatButton primary label="STAR" onClick={this.starBtnClick} value={searchRef.uid} />

    Unhandled Rejection (TypeError): Cannot read property 'starBtnClick' of undefined
    と言われます。
    どうしたらいいでしょうか?

    そもそもユーザーIDをカードに乗せたボタンに格納できているのか不明ですが。
    それが不能の場合、どのような方法があるでしょうか。

    キャンセル

  • 2017/10/27 23:43

    一応、一部分を取り出すとかでなければ .slice() は不要です。
    this.starBtnClick でエラーが出るのは、this が undefined になるからで、それは関数内だからです。
    楽な方法は bind することです。
    .map(function(searchRef){ ... }.bind(this))

    また、更新する箇所が1つなら
    var updates = {};
    などを用いてやる必要はありません。
    https://firebase.google.com/docs/database/web/lists-of-data#append_to_a_list_of_data

    キャンセル

  • 2017/10/28 00:09

    ありがとうございます。コンパイルは通ったのですが、

    addStarUserToDataBase() {
    const myUserId = this.state.userId
    var starUserData = this.state.willStarUser;
    var newStarUserRef = firebase.database().ref('/users/' + myUserId + '/starred_users/').push()
    var path = newStarUserRef.toString()
    newStarUserRef.set({
    path:starUserData
    })

    この newStarUserRef.set({
    の部分が、

    Error: Reference.set failed: First argument contains a function in property 'users.tkI5FZ6gizduCcdsIUwY1iObxUX2.starred_users.-KxTMmyDiuvvR2BAZcdk.path.currentTarget' with contents = function () {
    return arg;
    }

    こんな感じのエラーが出るのですが、何がダメなのかさっぱりです。

    キャンセル

  • 2017/10/28 00:14

    少し考察しましたが、もしかしたら、ボタンからvalueを取ってくるのは無理なのかもしれませんね。

    キャンセル

  • 2017/10/28 00:18

    セットしようとしてるデータに関数が含まれてますよ、と言っています。
    starBtnClick で willStarUser に event object をセットしているからではないでしょうか(ボタンクリックのイベントハンドラーには当然 event object が渡されます)。

    キャンセル

  • 2017/10/28 00:21

    値をボタンから持ってくるにはどうしたらいいでしょうか。

    キャンセル

  • 2017/10/28 00:21

    value属性などを使わずとも
    <FlatButton primary label="STAR" onClick={() => this.starBtnClick(searchRef.uid)} />
    とすればいいのではないでしょうか。

    キャンセル

  • 2017/10/28 00:43

    ありがとうございます!とりあえずスター済みかどうかに関わらず、無限にスターする機能は追加できました。

    同一のユーザーのスターをできるのは良くないので、スター済みの場合はボタンをSTARRDと表示させたいです。スター済みのユーザーかどうかを判定させるにはどうしたらいいでしょうか。

    キャンセル

  • 2017/10/28 01:30

    現在、スターされた数をデータベースに送る方法で困っています。

    /*------------add star user------------------------------------------------------------------------*/

    starBtnClick(value){
    this.setState({willStarUser:value},this.allAddFunction)
    }

    addStarUserToDataBase() {
    const myUserId = this.state.userId
    var starUserData = this.state.willStarUser;
    firebase.database().ref('/users/' + myUserId + '/starred_users/').push().set({
    starUserData
    })

    }

    resetWillStarUser(){
    this.setState({
    willStarUser:''
    });
    }

    allAddFunction(){
    const self = this
    this.addStarUserToDataBase(function() {
    self.nowStarCount(function(){
    self.addStar
    })
    },self.resetWillStarUser)
    }

    nowStarCount(){
    const self = this
    const willAddStar = this.state.willStarUser
    console.log('0')
    firebase.database().ref('users/' + willAddStar).once("value").then(function(snapshot){
    self.setState({
    starCount:snapshot.val().userStar
    })
    })
    }

    addStar(){
    const self = this
    var star = this.state.starCount
    var added = star + 1
    console.log('1')
    this.nowStarCount(function(){
    console.log('2')
    self.setState({starCount:added},self.sendStarData)
    })
    console.log(self.state.starCount)
    }

    sendStarData(){
    const self = this
    const willAddStar = this.state.willStarUser
    console.log(self.state.starCount)
    firebase.database().ref('/users/' + willAddStar).set({
    userStar:self.state.starCount
    })
    }

    同期しないと行けないと思い、このようにしたのですが、書いたconsole.logどこにも飛びません。

    何がおかしいのでしょうか。

    キャンセル

  • 2017/10/28 13:45

    まず、ボタンクリック時に this.willStarUser にユーザーIDをセットし他の関数でその値を使う、としていますが、少し回りくどいので直接他の関数に引数として渡したほうがいいです。

    また addStarUserToDataBase などに関数を渡していますが、定義を見ると引数が0個なので渡す意味がないですね。

    キャンセル

  • 2017/10/28 14:02

    また、そのコードでは現在のスター数を取得し、+1 して送信する、としていますが、これはデータベースにおける典型的なまずいやり方です。

    もしユーザーAがスター数を取得(nとする)してから増やして送るまでの間に、他のユーザーBもスターを増やそうとしてスター数を取得すると n が得られます。そしてA はスター数 n+1 を送ります。Bも遅れて n+1 を送ります。結局、2人がスターしたのにスター数は n+1 になります。

    このような事態を防ぐためにトランザクションというのを使う必要があります。
    https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions
    たまたまこれもスター数を更新する例ですが、もちろんコピペせずに適宜自分のコードに合わせて修正してください。

    キャンセル

  • 2017/10/28 14:59

    .then を使うと簡単に書けますよ。
    https://pastebin.com/qsS1Lr0N

    キャンセル

  • 2017/10/28 15:55

    starBtnClick(value){
    const self = this
    this.addStar(value)
    }

    addStar(uid) {
    const myUserId = this.state.userId
    const starUserData = uid
    firebase.database().ref('/users/' + myUserId + '/starred_users/').push().set({
    starUserData
    }).then(function(){
    return firebase.database().ref('users/' + uid + '/userStar').transaction(function(count) {
    if(count){
    if(count.userStar&&count.userStar[myUserId]){
    count.userStar--;
    count.userStar[myUserId] = null;
    }else{
    count.userStar++
    if (!count.userStar) {
    count.userStar = {};
    }
    count.userStar[myUserId] = true;
    }
    }
    return count
    })
    })
    }
    コードはこのようになりました。
    count.userStar[myUserId]
    この部分をうまく反映させたいのですが、そもそも、starの数がフォローしても増えません。どこがおかしいでしょうか。

    キャンセル

  • 2017/10/28 17:01 編集

    データベースをどういう形式にしようとしているのかよく分からないです。
    count.userStar++ は数字
    count.userStar = {}; はオブジェクト と矛盾していませんか?

    キャンセル

  • 2017/10/28 17:13

    https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions
    ここを参考に、starCount内にスターしたユーザーを格納した方が、二度目のスターを制限できると思い、
    真似て見たのですが、これだとダメだということですね。

    この中身でいうpost.stars[uid]はデータベース上ではどう定義してるんでしょうか。

    キャンセル

  • 2017/10/28 17:28

    その例だと、スターしたユーザーのIDを保存する"stars"と、スター数を保存する"starCount"に分かれていますよね。もちろんそれを1つにまとめることはできません。1つのキーに対しては1つの値しかもてません。

    > post.stars[uid]はデータベース上ではどう定義してるんでしょうか。
    obj["key"] が obj.key と同義なのはご存じですか?
    つまり uid をキーにして、値を適当に true にセットしています。
    こんな感じです。
    https://pastebin.com/41N7MMFk

    既にご存じでしたらいいですが、データベースの中身はコンソールからいつでも見れますので確認に使ってください。
    https://console.firebase.google.com/

    キャンセル

  • 2017/10/28 19:09

    ありがとうございます。スターされた側の制御は完璧に機能するようになりました。

    次に、二度スターを押された場合、スターユーザーのリストから消す。というのを
    .then(function(){
    if(add){
    return firebase.database().ref('/users/' + myUserId + '/starred_users/').push().set({
    starUserData
    })
    }else{
    return firebase.database().ref('/users/' + myUserId + '/starred_users/').orderByChild('starUserData').startAt(starUserData).remove()
    }
    })
    }

    removeで消せるようなので、このように実装したのですが、(全文は質問文のコードにあります)

    removeはrefした場所を消すらしく、検索機能で使った
    .orderByChild('starUserData').startAt(starUserData).
    のようなごまかしはきかないようです。消せません。

    ユーザーIDのパスが
    '/users/' + myUserId + '/starred_users/' + 重複しないキー
    の中に
    starUserData : スターしてるユーザーID

    のように管理されています。どうしたらいいでしょうか。

    一応、データ構造を一部省略し、JSONで書くと、

    '/users/' + myUserId + '/starred_users/' :{
    重複しないキー:{
    starUsarData : uid
    }
    重複しないキー:{
    starUsarData : uid
    }
    }
    みたいな感じです。

    キャンセル

  • 2017/10/28 20:51

    orderByChild と equalTo を組み合わせれば、特定のキーに特定の値を持つエントリーだけを取得できます。
    filtering メソッドを使用すると remove はできないようですね。
    .once("value", ) で snapshot を取得すると、
    重複しないキー:{
    starUsarData : uid
    }
    が1つだけ含まれているので、forEach でそれの snapshot を取得し、対応する ref を削除すればいいと思います(たぶん)。
    https://pastebin.com/WhpVbJH3

    キャンセル

  • 2017/10/28 21:13

    ありがとうございます!!!!!!!できました!!!!!
    あとはスターされてたらSTARRED スターしてなかったらSTARとするだけですね!頑張ります!

    キャンセル

  • 2017/10/28 21:51

    と思って、色々書いて見てたのですが、フォロー済みという情報をどうやって取って来ましょう。

    先ほどのstarUserDataと検索結果を照合して、判定させなければいけないのですが、
    hundleSearchのchildSnapshotがちょうどいいと思い、
    そこで判定しようと思います。
    childSnapshot.val().userStar.stars にて、uid : true という配列データを取ってこれるのですが、
    この、uidと自分のIDを判定する時、
    繰り返しの処理はrender内での出来事です。なので、判定し、ボタンを表示するかどうかという情報をchildSnapshotで繰り返しを行い判定したものを格納する必要があります。

    render内でのスター表示の判定は、searchRef.starBtnShownが0とか1とかで判定すればいいと思うのですが、(starBtnShownはそれぞれのユーザーがスター済みかどうかの判定を格納する配列)

    childSnapshot.val().userStar.stars にて、uid : true という配列データと自分のIDを判定する式が怪しいので聞きたいのと、
    いつもだと、:の右側のデータを取ってくるので、どうやって左側を受け取るのか、(あるいは、ユーザー IDでデータを取って来て、null、もしくはundefinedだったらスター済みじゃなく、trueだったらスター済みのように判定するのか。)

    どちらがやりやすいでしょうか。

    キャンセル

  • 2017/10/28 22:10

    > render内でのスター表示の判定は、searchRef.starBtnShownが0とか1とかで判定すればいいと思うのですが
    まぁ普通 true か false ですね。

    > いつもだと、:の右側のデータを取ってくるので、どうやって左側を受け取るのか、(あるいは、ユーザー IDでデータを取って来て、null、もしくはundefinedだったらスター済みじゃなく、trueだったらスター済みのように判定するのか。)
    さっきの star するときにも後者のやり方だったので、そちらでいいのではないでしょうか。

    キャンセル

  • 2017/10/28 23:10

    <div>
    {
    this.state.searchReference === '0'
    ? <div><h>here is search reference view</h></div>
    :<ul>{
    this.state.search_users.map(
    function(searchRef){
    return(
    <div className='col s12 m4'>
    <Card>
    <CardTitle title={searchRef.user} subtitle={searchRef.title} />
    <CardText>
    {searchRef.explain}
    </CardText>
    <CardActions>
    {
    function(){
    const self = this
    if(searchRef.userStar.stars[self.state.userId] == true){
    self.setState({starBtnShown : false})
    }else{
    self.setState({starBtnShown : true})
    }

    return(
    this.state.starBtnShown == true
    ?<FlatButton
    label="STAR"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    :<FlatButton
    backgroundColor="#a4c639"
    hoverColor="#8AA62F"
    label="STARRED"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    )}
    }
    </CardActions>
    </Card>
    </div>

    こうして見たのですが、全ての投稿からボタンが消えてしまいました。どこがおかしいでしょうか。
    また、この条件分岐は果たしてうまくかけてるでしょうか。

    キャンセル

  • 2017/10/28 23:58

    いや... starBtnShown を state に入れる必要がないですよね。
    しかもすべてのユーザーで starBtnShown という共通の state を使用してますが...。
    さらに、
    <CardActions>{ function(){ ... } }</CardActions>
    となってますが、関数を作っただけで実行していません。

    キャンセル

  • 2017/10/29 00:05 編集

    ごめんなさい。<>このようなタグの中で、関数を実行するにはどうしたら良いでしょうか。
    編集: <any></any>の間で、

    stateの件そうですよね。バカでした申し訳ない。

    キャンセル

  • 2017/10/29 00:14

    いまの場合関数を使わなくとも

    <CardActions>
    {
    searchRef.userStar.stars[self.state.userId]
    ?<FlatButton ... />
    :<FlatButton ... />
    }
    </CardActions>

    でいいのではないですか?

    キャンセル

  • 2017/10/29 00:20

    ありがとうございます。
    しかしどうやら別の問題があるようです。

    searchRef.userStar.stars[self.state.userId] は
    Unhandled Rejection (TypeError): Cannot read property 'tkI5FZ6gizduCcdsIUwY1iObxUX2' of undefined

    これによりダメみたいです。
    どうしたらいいんでしょうか。

    キャンセル

  • 2017/10/29 00:22

    searchRef.userStar.stars が undefined、つまり searchRef.userStar が stars というプロパティを持っていないということです。

    キャンセル

  • 2017/10/29 00:26

    なるほど。スターされたことがないユーザーはstarsプロパティを持ってないのも存在してしまうんですよね。
    どうやって解消するのがいいでしょうか。

    キャンセル

  • 2017/10/29 00:35

    stars プロパティが存在しかつ自分のidが含まれるときに、"STARRED"と表示すればいいのではないでしょうか。

    (searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId])
    ?<FlatButton label="STARRED" ... />
    :<FlatButton label="STAR" ... />
    }

    キャンセル

  • 2017/10/29 00:39

    ありがとうございます!!!!できました!!!

    キャンセル

  • 2017/10/29 00:42

    リアルタイムに反応するようにしたいところですが、導入が大変そうですね。

    次はポストのスター機能を同様につけてゆき、コメント機能を作成していきます。

    キャンセル

  • 2017/10/30 00:02

    作成中デバックをしていたのですが、やっぱり、STARボタンを押したら、リアルタイムでSTARREDとならないとおかしいことに気づきました。どうしたらいいでしょう。

    キャンセル

  • 2017/10/30 00:32

    https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
    ここを見ると分かるように、.transaction() は Promise を返し、その resolve 値はオブジェクトで、snapshot プロパティに更新された snapshot をもつので、こうすればいいんじゃないでしょうか。
    }).then(function () {
    if (add) { return ....transaction(...) } else { ... }
    }).then(function (result) {
    if (result.snapshot) {
    const updatedUser = snapshot.val()
    // state を更新
    }
    }

    キャンセル

  • 2017/10/30 15:54

    ありがとうございます。stateを更新して、それで判定すればいいと思うんですけど、
    その場合、現在のボタン判定の式
    (searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId])
    をstateでの判定の式に取り替えたらいいんでしょうか?
    現在のコードをこの次にあげます。

    キャンセル

  • 2017/10/30 15:57

    starBtnClick(value){
    this.addStar(value)
    }

    addStar(uid) {
    const myUserId = this.state.userId
    const starUserData = uid
    var add = true
    firebase.database().ref('users/' + uid + '/userStar').transaction(function(count) {
    if(count){
    if(count.stars&&count.stars[myUserId]){
    count.starCount--;
    count.stars[myUserId] = null
    add = false
    }else{
    count.starCount++
    if (!count.stars) {
    count.stars = {};
    }
    count.stars[myUserId] = true
    add = true
    }
    }
    return count
    }).then(function (result) {
    if (result.snapshot) {
    const updatedUser = snapshot.val()
    //state updates
    self.setState({starBtnShown:updatedUser})
    }
    }.then(function(){
    if(add){
    return firebase.database().ref('/users/' + myUserId + '/starred_users/').push().set({
    starUserData
    })
    }else{
    return firebase.database().ref('/users/' + myUserId + '/starred_users/').orderByChild('starUserData').equalTo(starUserData).once("value", function (snapshot) {
    snapshot.forEach(function (child) {
    child.ref.remove()
    })
    })
    }
    })
    }

    キャンセル

  • 2017/10/30 15:58

    this.state.search_users.map(
    function(searchRef){
    const self = this
    return(
    <div className='col s12 m4'>
    <Card>
    <CardHeader
    title={searchRef.user}
    subtitle={" star:(" + searchRef.userStar.starCount + ")"}
    avatar=""
    />
    <CardTitle title={searchRef.title} />
    <CardText>
    {searchRef.explain}
    </CardText>
    <CardActions>
    {
    (searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId])
    ?<FlatButton
    backgroundColor="#FDD835"
    hoverColor="#8AA62F"
    label="STARRED"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    :<FlatButton
    label="STAR"
    labelColor="#FFFB3B"
    backgroundColor="#EOEOEO"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    }
    </CardActions>
    </Card>
    </div>

    キャンセル

  • 2017/10/30 15:58

    this.state.search_users.map(
    function(searchRef){
    const self = this
    return(
    <div className='col s12 m4'>
    <Card>
    <CardHeader
    title={searchRef.user}
    subtitle={" star:(" + searchRef.userStar.starCount + ")"}
    avatar=""
    />
    <CardTitle title={searchRef.title} />
    <CardText>
    {searchRef.explain}
    </CardText>
    <CardActions>
    {
    (searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId])
    ?<FlatButton
    backgroundColor="#FDD835"
    hoverColor="#8AA62F"
    label="STARRED"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    :<FlatButton
    label="STAR"
    labelColor="#FFFB3B"
    backgroundColor="#EOEOEO"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    }
    </CardActions>
    </Card>
    </div>

    キャンセル

  • 2017/10/30 16:01

    それと、この状態ではsnapshotがnotdefinedとなるようです。

    キャンセル

  • 2017/10/30 16:15

    どこの snapshot のことでしょうか?

    キャンセル

  • 2017/10/30 16:24 編集

    最後の then の前に ")" が抜けてますよ。

    キャンセル

  • 2017/10/30 21:17

    thenの )了解です。修正済みです。

    result.snapshotの部分です。

    キャンセル

  • 2017/10/30 21:34

    それに加えて現在、ユーザーのポストをクリックしたら、その、ユーザーページに飛ぶように機能させたいのですが、とりあえずそのビューのuserPage.jsを作っています。

    この時、ポストのユーザーIDを参照して、ページに飛ぶといいと思うのですが、この場合、urlの管理はuserpage/uid/として表示したほうがいいですよね?その場合は、ルーティングをどう行ったらいいでしょうか。
    また、そのユーザーのIDを受け取って、userPage.jsを開いたらいいと思うのですが、どう受け取らせたら良いでしょうか。ファイル間の受け渡しかたがわかりません。

    キャンセル

  • 2017/10/30 23:29

    あっすいません。凡ミスです。
    const updatedUser = snapshot.val()
    ではなく
    const updatedUser = result.snapshot.val()
    でした。これで直りますか?

    キャンセル

  • 2017/10/30 23:36

    コンパイル通るようになりました。
    ボタンの表示の判定はどうしたらいいでしょう。試しに、

    {
    (this.state.starBtnShown)
    ?<FlatButton
    backgroundColor="#FDD835"
    hoverColor="#8AA62F"
    label="STARRED"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    :<FlatButton
    label="STAR"
    textColor="#FFFB3B"
    backgroundColor="#EOEOEO"
    onClick={() => this.starBtnClick(searchRef.uid)} />
    }

    こうしてしまったら、一回押すと全てがSTARREDになってしまいます。
    元の、

    searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId]

    これも使うのでしょうか。
    うまくいかないです。

    キャンセル

  • 2017/10/30 23:38

    > その場合は、ルーティングをどう行ったらいいでしょうか。
    また、そのユーザーのIDを受け取って、userPage.jsを開いたらいいと思うのですが、どう受け取らせたら良いでしょうか。ファイル間の受け渡しかたがわかりません。

    ファイル間というよりcomponentへの渡し方ですね。
    react-routerの機能でurlパラメーターを渡すというのがあります。
    https://reacttraining.com/react-router/web/api/Route/component
    たとえば <Route path="/book/:id" component={Book}/> とすれば、Book コンポーネントに":id"の部分のパラメーターが渡され、this.props.match.params.id で取り出せます。
    公式の例
    https://reacttraining.com/react-router/web/example/url-params

    キャンセル

  • 2017/10/30 23:43

    > 一回押すと全てがSTARREDになってしまいます。
    それは this.state.starBtnShown を全部のユーザーで共有してるからですね。
    先程の result.snapshot.val() がなんの値か理解されていますか?
    それを state の適切な場所に入れればいいんですが...。

    キャンセル

  • 2017/10/31 00:00

    鵜呑みにしてコピペしていて理解が追いついていませんでした。考え方を整理させていただいてよろしいでしょうか。

    最初に、今までの判定通り、
    searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId]
    これで、検索前にstarされていたか否かを判定すると思うので、これは残しますよね。

    次に、ボタンを押したら、トランザクションのpromiseのresolveをオブジェクトで受け取ります。この中身をresult.snapshot.val()をコンソールで確認すると、starCount : 0というデータが入っています。

    これをどのように使って判定したらいいでしょうか。

    そしてstarBtnShownを共有してしまっていたようなのですが、配列型の定義をした方が良かったでしょうか。そうでないのなら、繰り返しのfunction(searchRef)内のstateをどのように一つずつ区別したらよろしいでしょうか。

    キャンセル

  • 2017/10/31 00:00

    ルーティングの件:


    ありがとうございます。その通り、作成してみます。

    キャンセル

  • 2017/10/31 00:04

    ところで、これは全然機能に問題のないwarningなのですが、material uiのtextFieldをマルチラインに対応させようと、
    multiLine={true}
    この1行を追加したのですが、

    No duplicate props allowed react/jsx-no-duplicate-props

    このようにwarningされます。
    表示はマルチラインになっており、機能には問題ないのですが、consolemに黄色い画面がたくさん増えて気持ち悪いのですが、どうやったら改善できますでしょうか。

    キャンセル

  • 2017/10/31 00:23

    state はなるべく簡単な構造にしておいたほうがいいです。
    result.snapshot.val() が何かというと、transaction を呼び出した
    ref('users/' + uid + '/userStar') の新しい値です。

    現在、
    this.state.search_users 内の各 searchRef について、
    searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId]
    が true か false かでボタンの表示が変わります。
    そして searchRef.userStar はデータベースの 'users/' + id + '/userStar' から取ってきた値です(確か)。

    該当する searchRef の userStar プロパティを新しい値で置き換えればいいと思いませんか?
    this.state.search_users の中から該当するのを見つけるには、一つ一つ uid をチェックする必要がありますね。

    キャンセル

  • 2017/10/31 00:26

    > No duplicate props allowed react/jsx-no-duplicate-props

    よく分からないですが、1つの tag に同じ prop を複数指定しているんじゃないでしょうか。
    https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md

    キャンセル

  • 2017/10/31 00:51 編集

    混乱しているので確認ですが、
    searchRef.userStar.stars && searchRef.userStar.stars[self.state.userId] は 検索結果の全てのユーザーの中で、スターされてるユーザー かつ その中に自分のユーザーIDがある。の状態です。

    この判定を、新しいstateで置き換えて、同じ意味にする場合、
    stateを配列にし、 

    uid : {
    userstar : {
    stars:{
    ... : ...
    }
    starCount : 1
    }
    }
    このまま配列に入れていけばいいでしょうか。
    そして、このstateを

    searchRef.this.state.starBtnShown[uid].stars && searchRef.this.state.starBtnShown[uid].stars[self.state.userId]

    このようにしたら、上の条件式を満たしていて、なおかつボタンに同期的になるでしょうか。

    キャンセル

  • 2017/10/31 00:54

    > No duplicate props allowed react/jsx-no-duplicate-props

    の件ありがとうございます。
    自分でも信じられないのですが、二回同じのを書いていて、それを見つけられませんでした。笑

    それともう一つ謎のwarningがあり、
    proxyConsole.js:54 Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Home`. See https://fb.me/react-warning-keys for more information.
    in div (at Home.js:294)
    in Home (created by Route)
    in Route (at App.js:56)
    in Switch (at App.js:55)
    in div (at App.js:52)
    in MuiThemeProvider (at App.js:51)
    in Router (created by BrowserRouter)
    in BrowserRouter (at App.js:50)
    in App (at index.js:8)
    とのことですが、何が悪いかさっぱりわかりません。

    キャンセル

  • 2017/10/31 01:03 編集

    searchRef.this というのは意味不明ですね...。

    私が言いたいのは、this.state.search_users の中で該当するユーザーの userStar プロパティ を新しい値で置き換えれば、あとは render 関数で勝手に判定して表示が更新されますよね、ということです。
    state に新しく項目を追加する必要はありません。

    キャンセル

  • 2017/10/31 01:22

    ありがとうございます。ようやく理解できました。

    ところでここで基礎知識不足の僕がそうするコードを書こうとすると、
    self.setState({search_users.uid.userStar:updatedUser})
    このように書いてしまいました。
    自分の中では該当プロパティの値を新しい値にしてくれ、とやっているつもりなのですが、できないようですね。

    stateの該当する一部を変更するためにはどのようにしたらよろしいでしょうか。また、stateに限らず、配列の該当部の子を今回のように更新するときはどうしたらよろしいでしょうか。

    キャンセル

  • 2017/10/31 01:57

    ネストしたオブジェクトの深いところを更新するのは、実は割と面倒です。
    なぜなら react では this.state を mutate してはいけないからです。
    なのでオブジェクトをコピーして、それの一部を更新し、setState で代入する必要があります。
    幸いヘルパー関数があるのでそれを使えば楽です。
    https://github.com/kolodny/immutability-helper
    (npm install する必要があります)

    this.search_users は配列ですから、this.search_users.uid と取り出すことはできません。
    どのユーザーが該当のユーザーかをループして調べる必要があります。
    調べた結果インデックスが i になったとすれば、次のようにすればたぶん更新できます。

    const newSearchUsers = update(this.state.search_users, {
    [i]: {userStar: {$set: someNewValue} }
    });
    this.setState({search_users: newSearchUsers});

    {[i]: ...} という記法をご存じないのでしたらこちらを参考にしてください。
    https://qiita.com/kmagai/items/95481a3b9fd97e4616c9