Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

R言語で相関係数

$
0
0

2つの数字データがあって、相関ってあるの?っていうのが知りたい。そんなときに使えるのが相関係数というものみたいです。

相関係数を求める前に共分散というのを求める必要があります。共分散というのは、2つのデータの偏差(平均と引いた数)を掛け算して平均をとったものみたいです。R的に書くとこんな感じ。

x <- 1:10
y <- 101:110

z <- mean((x - mean(x)) * (y - mean(y)))

1~10と101~110までという、とても強い正の相関を持った数字で試してみました。zが共分散です。zには8.25というとても数字が入りました。数字的にも正の相関がありそうですね。

共分散だと、同じような相関のありそうなデータでも単位が変わると値が変わっちゃうという欠点があります。

x <- c(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1)
y <- 101:110

z <- mean((x - mean(x)) * (y - mean(y)))

これだとzが0.825になっちゃいます。

じゃぁ1~-1の間に正規化してやろうというのが相関係数になります。求め方は共分散を2つのデータの標準偏差を掛け合わせたもので割ります。標準偏差は、偏差の2乗の平均の√だったのでRで書くとこんな感じですね。

x <- c(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1)
y <- 101:110

z <- mean((x - mean(x)) * (y - mean(y)))
sdx <- sqrt(mean((x - mean(x))^2))
sdy <- sqrt(mean((y - mean(y))^2))
answer <- z / (sdx * sdy)

answerには1という結果が入ります。正の相関があるって感じですね。こういう感じに相関が逆っぽいデータをつっこむとanswerは-1になります。

x <- -1:-10
y <- 101:110

z <- mean((x - mean(x)) * (y - mean(y)))
sdx <- sqrt(mean((x - mean(x))^2))
sdy <- sqrt(mean((y - mean(y))^2))
answer <- z / (sdx * sdy)

Rは統計関係の関数をたっぷりもってるので(標準偏差はsdって関数でしたよね)ここで求めた共分散や相関係数も関数で持ってます。covとcorがそれになります。covとsdを使うとさっきのプログラムはこんな感じになります。

x <- -1:-10
y <- 101:110

answer <- cov(x, y) / (sd(x) * sd(y))

corを使うとこうですね。

x <- -1:-10
y <- 101:110

answer <- cor(x, y)

Friendlyを入門してみた

$
0
0

デスクトップアプリのテストを行うためのFriendlyというライブラリのハンズオンの補助講師してきた(受講者に非常に近い立ち位置で)のでちょっと試してみました。WPFの足し算アプリを用意してみた。XAMLがわかればテストできる感じなので、XAMLだけさくっとさらしておきます。

<Window x:Class="WpfApplication4.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfApplication4"mc:Ignorable="d"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainWindowViewModel /></Window.DataContext><StackPanel><TextBox Text="{Binding Lhs.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/><TextBox Text="{Binding Rhs.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/><Button Content="Click"Command="{Binding AddCommand}"/><TextBlock Text="{Binding Answer.Value}" /></StackPanel></Window>

WPFのテストをするならRM.Friendly.WPFStandardControlsをNuGetから参照追加するといい感じです。

左辺値右辺値を入れてボタンを押すと答えが出る感じです。一応入力値チェックしてて、正しい入力をしないとボタンが押せなくしてます。Friendlyでテストするときは、Processをスタートしてアタッチするという感じのコードになります。プロセスの起動はProcess.Startでさくっとやります。

そして、WindowsAppFriendというクラスにProcessを渡すことで内部でいじくれるようにしてくれてます。

this.process = Process.Start(@"WpfApplication4.exe");
this.app = new WindowsAppFriend(this.process);

このWindowsAppFriendのTypeメソッドで相手プロセス内にある型をひっこぬいてくることができます。文字列指定や型引数指定などの方法がありますが、ここでは文字列指定でWPFのApplication型をひっこぬいてきました。ApplicationがとれたらCurrentのMainWindowでテスト対象アプリのWindowにアクセスできます。

app.Type("System.Windows.Application").Current.MainWindow

Typeメソッドの戻り値はdynamic型なのでなんでも呼び出せる!というのもいいんですがタイプセーフな感じのものでラップしてやるとテストが捗ります。先ほどのMainWindowをラップする感じでこんなクラスを定義します。WindowControlのLogicalTree拡張メソッドで論理ツリーがとれて、それに対してBindingのパスでコントロールを探せます。

publicclass MainWindowDriver
{
    public WPFTextBox Lhs { get; }
    public WPFTextBox Rhs { get; }
    public WPFButtonBase Add { get; }
    public WPFTextBlock Answer { get; }

    public MainWindowDriver(dynamic window)
    {
        var w = new WindowControl(window);
        this.Lhs = new WPFTextBox(w.LogicalTree().ByBinding("Lhs.Value").Single());
        this.Rhs = new WPFTextBox(w.LogicalTree().ByBinding("Rhs.Value").Single());
        this.Add = new WPFButtonBase(w.LogicalTree().ByBinding("AddCommand").Single());
        this.Answer = new WPFTextBlock(w.LogicalTree().ByBinding("Answer.Value").Single());
    }
}

このクラスを使ったテストの全体はこんな感じ。割とさくっと書けました。

using Codeer.Friendly.Dynamic;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Windows.Grasp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RM.Friendly.WPFStandardControls;
using System.Diagnostics;

namespace UnitTestProject1
{
    [TestClass]
    publicclass UnitTest1
    {
        private Process process;
        private WindowsAppFriend app;
        private MainWindowDriver driver;

        [TestInitialize]
        publicvoid Initialize()
        {
            this.process = Process.Start(@"WpfApplication4.exe");
            this.app = new WindowsAppFriend(this.process);
            this.driver = new MainWindowDriver(app.Type("System.Windows.Application").Current.MainWindow);
        }

        [TestCleanup]
        publicvoid Cleanup()
        {
            this.app.Dispose();
            this.process.Kill();
        }

        [TestMethod]
        publicvoid TestMethod1()
        {
            this.driver.Lhs.EmulateChangeText("10");
            this.driver.Rhs.EmulateChangeText("3");
            this.driver.Add.EmulateClick();
            Assert.AreEqual("13", this.driver.Answer.Text);
        }

        [TestMethod]
        publicvoid TestMethod2()
        {
            this.driver.Lhs.EmulateChangeText("aaa");
            this.driver.Rhs.EmulateChangeText("3");
            this.driver.Add.EmulateClick();
            Assert.AreEqual("0", this.driver.Answer.Text);
        }
    }

    publicclass MainWindowDriver
    {
        public WPFTextBox Lhs { get; }
        public WPFTextBox Rhs { get; }
        public WPFButtonBase Add { get; }
        public WPFTextBlock Answer { get; }

        public MainWindowDriver(dynamic window)
        {
            var w = new WindowControl(window);
            this.Lhs = new WPFTextBox(w.LogicalTree().ByBinding("Lhs.Value").Single());
            this.Rhs = new WPFTextBox(w.LogicalTree().ByBinding("Rhs.Value").Single());
            this.Add = new WPFButtonBase(w.LogicalTree().ByBinding("AddCommand").Single());
            this.Answer = new WPFTextBlock(w.LogicalTree().ByBinding("Answer.Value").Single());
        }
    }
}

FriendlyでFrameを使ったアプリをテストする

$
0
0

追記

blog.okazuki.jp

この記事の内容より良い方法がありましたので上の記事に書いてます。

あと、この記事に書いたWPFContentControl足してもらえたみたいです。

元記事

という要望がありました。

WindowControlのLogicalTreeで辿ればいいや!って思ってたら、FrameはContentにPageを持つからPageの中はLogicalTree切れてるので検索できないっぽいですね?(自分では試してない)

ということなので、Frameをとってきて、そいつのContentのLogicalTreeを辿ればいいということになります。FriendlyのWPF用のパッケージには、FrameやらPageやらを操作するためのクラスがないということで、無いならまずは作ります。FrameもPageもContentControlなので、WPFContentControlというクラスを用意します。

class WPFContentControl : WPFControlBase4<ContentControl>
{
    public WPFContentControl(AppVar c) : base(c)
    {
    }

    public AppVar Content { get { returnthis.Dynamic().Content; } }
}

あとは、こんな雰囲気でFrameをとってContentをとってContentのLogicalTreeから目的のコントロールをとる感じでいけます。さくっと作ったボタン押したらHello worldってなるアプリだとこんな感じ。ButtonとかTextBlockとかのとり方はアプリに合わせて変わるので、よしなに。

[TestClass]
publicclass UnitTest1
{
    private Process process;
    private WindowsAppFriend app;

    [TestInitialize]
    publicvoid Initialize()
    {
        this.process = Process.Start("WpfApplication5.exe");
        this.app = new WindowsAppFriend(this.process);
    }

    [TestCleanup]
    publicvoid Cleanup()
    {
        this.app.Dispose();
        this.process.Kill();
    }

    [TestMethod]
    publicvoid TestMethod1()
    {
        var window = new WindowControl(app.Type<Application>().Current.MainWindow);
        // Point1 Frameをとる
        var frame = new WPFContentControl(window.LogicalTree().ByType<Frame>().Single());
        // Point2 FrameのContentをとる(こいつがPage)
        var page = new WPFContentControl(frame.Content);
        // Point3 PageのLogicalTreeをとってごにょごにょする
        var btn = new WPFButtonBase(page.LogicalTree().ByType<Button>().Single());
        var txt = new WPFTextBlock(page.LogicalTree().ByType<TextBlock>().Single());
        Assert.AreEqual("", txt.Text);
        btn.EmulateClick();
        Assert.AreEqual("Hello world", txt.Text);
    }
}

WPFContentControlくらいは標準で入っててほしかったかも?

FriendlyでFrameを使ったアプリをテストする その2

$
0
0

blog.okazuki.jp

1つ前の記事でWPFContentControlなるものをこさえてまでやった方法ですが、Friendly作者の石川さんに聞いたらさくっともっといい方法を教えてもらえました。

AppVar型にもLogicalTreeメソッドはあるのじゃよ

個人的にWPFContentControlを作ったのはLogicalTreeメソッドとかを使いたかったからでした。AppVar型にもLogicalTree拡張メソッドがあるということを教えてもらったのでこう書けます。

var window = new WindowControl(app.Type<Application>().Current.MainWindow);
// Point1 Frameをとって、そのContentをとってAppVarに入れる
AppVar page = window.LogicalTree().ByType<Frame>().Single().Dynamic().Content;
// Point2 PageのLogicalTreeをとってごにょごにょする
var btn = new WPFButtonBase(page.LogicalTree().ByType<Button>().Single());
var txt = new WPFTextBlock(page.LogicalTree().ByType<TextBlock>().Single());
Assert.AreEqual("", txt.Text);
btn.EmulateClick();
Assert.AreEqual("Hello world", txt.Text);

VisualTreeを辿る

論理ツリーが切れてるならVisualTreeを辿ればいいじゃない?ということで、以下のような感じに書けます。

var window = new WindowControl(app.Type<Application>().Current.MainWindow);
// Point1 VisualTreeからPageをとる
var page = window.VisualTree().ByType<Page1>().Single();
// Point2 PageのLogicalTreeをとってごにょごにょする
var btn = new WPFButtonBase(page.LogicalTree().ByType<Button>().Single());
var txt = new WPFTextBlock(page.LogicalTree().ByType<TextBlock>().Single());
Assert.AreEqual("", txt.Text);
btn.EmulateClick();
Assert.AreEqual("Hello world", txt.Text);

すっきり!

めとべや東京 10でUWPの新しいバインディングについて話してきました

ReactiveProperty v2.4.0-pre1を放流しました

$
0
0

www.nuget.org

ReactiveProperty v2.4.0-pre1をNuGetに放流しました。 今回は、Xamarin.iOSのUIViewに対してSetBindingメソッドを使えるようにしたりしました。Xamarin.Androidにしか今までなかった機能をポーティングした感じです。が…、手元にiOSとMacがないのでテストできてないので誰か試してみてくれると嬉しいです…。

ReactiveProperty v2.4.1をリリースしました

$
0
0

www.nuget.org

クリスマスリリース!というわけでもなく、バグっぽい挙動の報告がきたのでなおしました。はい。

v2.4.1

バグフィックス

  • ReadOnlyReactiveCollectionでDispose時にコレクションの要素のDisposeが呼ばれないケースがある問題に対応

Visual Studio上のTypeScript JSXでReactのHelloWorldしてみた

$
0
0

試行錯誤した結果です。

Reactは言わずと知れたFacebookが作ってるライブラリですが、最近Visual Studio 2015 Update 1でtsxというTypeScriptでJSXをサポートするみたいなものが出てきました。今回はこれを使って遊んでみたいと思います。

node.jsのインストール

NuGetから入る型定義ファイルが、どうもイマイチうまくいかなかったのでnode.jsを入れてtsdコマンドを使うためにNode.jsを入れます。

Node.js

上記リンクから最新版を入れましょう。入れたら以下のコマンドを打ち込んでtsdを入れておきます。

npm install tsd -g

TypeScriptのプロジェクトの作成

TypeScript を使用した HTML アプリケーションを新規作成します。名前はReactHelloWorldにします。

以下のファイルをさくっと消します。

  • app.css
  • app.ts
  • index.html

プロジェクトの設定

プロジェクトのプロパティを開いて、TypeScriptビルドを開いて以下の項目を変更します。

  • TSXファイルでのJSXコンパイルを「応答」に変更
  • モジュールシステムを「CommonJS」に変更

型定義ファイルの取得

パッケージマネージャーコンソールを起動してプロジェクトフォルダに移動してtsd install react-globalと打ち込んで型定義ファイルを取得します。

PM> cd .\ReactHelloWorld
PM> tsd install react-global --save

 - react    / react-global                         
   -> react > react                                
   -> react > react-dom                            
   -> react > react-addons-create-fragment         
   -> react > react-addons-css-transition-group    
   -> react > react-addons-transition-group        
   -> react > react-addons-linked-state-mixin      
   -> react > react-addons-perf                    
   -> react > react-addons-pure-render-mixin       
   -> react > react-addons-test-utils              
   -> react > react-addons-update                  

>> running install..

>> written 11 files:

    - react/react-addons-create-fragment.d.ts
    - react/react-addons-css-transition-group.d.ts
    - react/react-addons-linked-state-mixin.d.ts
    - react/react-addons-perf.d.ts
    - react/react-addons-pure-render-mixin.d.ts
    - react/react-addons-test-utils.d.ts
    - react/react-addons-transition-group.d.ts
    - react/react-addons-update.d.ts
    - react/react-dom.d.ts
    - react/react-global.d.ts
    - react/react.d.ts

型定義ファイルをプロジェクトに追加します。

f:id:okazuki:20151226124604p:plain

ファイルの作成

TypeScript JSXファイルをhelloworld.tsxという名前で作成します。 そして、tsd.d.tsをドラッグしてreferenceタグを追加します。

そして、以下のようなHello worldを追加します。

/// <reference path="typings/tsd.d.ts" />interface HelloWorldProps {
    name:string;
    age: number;
}

class HelloWorldComponent extends React.Component<HelloWorldProps, any> {
    render() {
        return<div>Name: {this.props.name}, Age: {this.props.age}</div>;
    }
}

ReactDOM.render(
    <HelloWorldComponent name="okazuki" age={34} />,
    document.getElementById("content"));

次に、index.htmlを作成して以下のような感じにします。

<!DOCTYPE html><html><head><metahttp-equiv="Content-Type"content="text/html; charset=utf-8"/><title></title><metacharset="utf-8" /></head><body><divid="content"></div><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script><scriptsrc="helloworld.js"></script></body></html>

実行して動作確認

実行すると以下のような感じにばっちり表示されました。

f:id:okazuki:20151226125414p:plain


Visual Studio上でTypeScript JSXを使ってReact.js「ローカルにreact.jsのファイルを抱え込もう」

$
0
0

先ほどは、CDN上のReactを読み込みましたが、手元にreactのjsファイルを持ってくる場合。

bowerを使いました。(Visual Studio関係ない)

npm install bower -g

と入力してbowerを入れます。bowerはgitを使うみたいなので、gitをインストールしてPATHを通しておきます。

TypeScriptのプロジェクトを作ってパッケージマネージャーコンソールでプロジェクトのディレクトリに移動して以下のコマンドを打ち込んで必要なファイルを仕入れます。

tsd install react --save
bower install react

これでtypingsフォルダとbower_componentsフォルダが作られた、そこにd.tsファイルやjsファイルが追加されます。ソリューションエクスプローラですべてのファイルを表示するモードにして、typingsとbower_componentsフォルダをプロジェクトに含めます。

プロジェクトでTypeScriptのJSXのコンパイルを有効にして以下のようなapp.tsxを用意してお試し。

/// <reference path="typings/tsd.d.ts" />

var reactElement = React.createElement('h1', { className: 'header' }, 'これはReactです');
ReactDOM.render(reactElement, document.getElementById('content'));

HTMLはこんな感じで

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8" /><title>TypeScript HTML App</title></head><body><divid="content"></div><scriptsrc="bower_components/react/react.min.js"></script><scriptsrc="bower_components/react/react-dom.min.js"></script><scriptsrc="bower_components/react/react-with-addons.min.js"></script><scriptsrc="app.js"></script></body></html>

これで、ローカルに全部必要なファイルを抱え込んだプロジェクトができました。

Visual Studio上でTypeScript JSXを使ってReact.js「低レベルなタグの組み立て」

$
0
0

createElementメソッドを使ってせっせとタグを組み立てることができます。タグ名、属性、子要素という感じみたいです。

/// <reference path="typings/tsd.d.ts" />

var h1 = React.createElement('h1', { className: 'header' }, 'これはReactです');
var p = React.createElement('p', null, 'Reactで快適なSPA生活を!');
var section = React.createElement('section', null, h1, p);
ReactDOM.render(section, document.getElementById('content'));

これで

<section><h1class="header">これはReactです</h1><p>Reactで快適なSPA生活を!</p></section>

こんな感じのタグが組み立てられます。

Visual Studio上のTypeScript JSXを使ってReact.js「createFactory」

$
0
0

同じ名前のタグを沢山組み立てるときに楽ができるReact.createFactoryという関数があるみたい。こんな感じで、liタグを沢山作るときにあらかじめliタグを作るファクトリを定義することができる。

/// <reference path="typings/tsd.d.ts" />

var h1 = React.createElement('h1', { className: 'header' }, 'React!!');
var liFactory = React.createFactory('li');
var items =
    [
        liFactory({ className: 'item' }, 'item1'),
        liFactory({ className: 'item' }, 'item2'),
        liFactory({ className: 'item' }, 'item3'),
        liFactory({ className: 'item' }, 'item4')
    ];
var ul = React.createElement('ul', { className: 'list' }, items);

var section = React.createElement('section', { className: 'container' }, h1, ul);

ReactDOM.render(section, document.getElementById('content'));

これで、こんな感じのHTMLが生成された。

<sectionclass="container"data-reactid=".0"><h1class="header"data-reactid=".0.0">React!!</h1><ulclass="list"data-reactid=".0.1"><liclass="item"data-reactid=".0.1.0">item1</li><liclass="item"data-reactid=".0.1.1">item2</li><liclass="item"data-reactid=".0.1.2">item3</li><liclass="item"data-reactid=".0.1.3">item4</li></ul></section>

Visual Studio上のTypeScript JSXを使ってReact.js「便利なファクトリメソッド」

$
0
0

React.jsはReact.DOMに各種タグを簡単につくるためのファクトリメソッドが定義されています。使ってみましょう。

/// <reference path="typings/tsd.d.ts" />

var section = React.DOM.section(
    { className: 'container' },
    React.DOM.h1({ className: 'header' }, 'React!!'),
    React.DOM.ul(
        { className: 'list' },
        React.DOM.li({ className: 'item' }, 'item1'),
        React.DOM.li({ className: 'item' }, 'item2'),
        React.DOM.li({ className: 'item' }, 'item3'),
        React.DOM.li({ className: 'item' }, 'item4')));

ReactDOM.render(section, document.getElementById('content'));

React.DOM.タグ名でいけるっぽいですね。便利。

Visual Studio上のTypeScript JSXを使ってReact.js「JSXらしい書き方してみよう」

$
0
0

今まで別にTypeScript JSXじゃなくてもいい書き方をしてたのですが、ついにJSXらしい書き方をしてみようと思います。

このコードを

/// <reference path="typings/tsd.d.ts" />

var section = React.DOM.section(
    { className: 'container' },
    React.DOM.h1({ className: 'header' }, 'React!!'),
    React.DOM.ul(
        { className: 'list' },
        React.DOM.li({ className: 'item' }, 'item1'),
        React.DOM.li({ className: 'item' }, 'item2'),
        React.DOM.li({ className: 'item' }, 'item3'),
        React.DOM.li({ className: 'item' }, 'item4')));

ReactDOM.render(section, document.getElementById('content'));

JSXで書くとこうなります。

/// <reference path="typings/tsd.d.ts" />

var section =
    <section className='container'>
        <h1 className='header'>React!!</h1>
        <ul className='list'>
            <li className='item'>item1</li>
            <li className='item'>item2</li>
            <li className='item'>item3</li>
            <li className='item'>item4</li>
            </ul>
        </section>;

ReactDOM.render(section, document.getElementById('content'));

インデントが変なのはVisual Studio先生がそうさせたから。まだこなれてないっぽいですね。

とりあえず最初の感想としては、なにこれキモイ。

Visual Studio上のTypeScript JSXを使ってReact.js「Hello ReactComponnent」

$
0
0

今まではタグを組み立ててただけですが、今回はReactComponentというものを作ります。

propsとstateというものをReactComponentを持ってるみたいですが、propsはイミュータブルでstateに変更する予定のある値を突っ込むみたいなイメージっぽいです。ReactComponentを作るには、React.Component<TProps, TState>を継承したクラスを作ります。TPropsにpropsのインターフェース、TStateにstateのインターフェースを指定します。

とりあえず、今回はボタンをクリックすることでh1タグの要素を消したり出したりする簡単なものを作ってみたいと思います。textプロパティでh1タグに表示する内容を指定できるようにしてみたいと思います。

まず、TPropsに指定するプロパティのインターフェースを定義します。textという文字列のプロパティを持っただけのインターフェースです。

interface ToggleComponentProperties {
    text:string;
}

次に、TStateに指定するインターフェースを定義します。これはh1を表示するか非表示にするかというvisibleというboolean型のプロパティを持たせることにします。

interface ToggleComponentState {
    visible: boolean;
}

次に、React.Componentを継承したクラスを定義します。

class ToggleComponent extends React.Component<ToggleComponentProperties, ToggleComponentState> {
}

このクラスにはコンストラクタでpropsを受け取るのとstateを初期化する処理を書く必要があります。

constructor(props: ToggleComponentProperties) {
    super(props);
    this.state = { visible: true } as ToggleComponentState;
}

visibleを切り替えるボタンクリックのイベントハンドラを用意しておきます。

handleClick(): boolean {
    this.setState({ visible: !this.state.visible } as ToggleComponentState);
    returnfalse;
}

そして、renderでタグの組み立てを行います。visibleがtrueの時はsectionにh1とbuttonを出して、falseのときにはsectionにbuttonだけを置くようにしてます。

render() {
    var header = <h1 className='header'>{this.props.text}</h1>;
    var button = <button onClick={this.handleClick.bind(this) }>Toggle!</button>;

    if (this.state.visible) {
        return<section>
                {header}
                {button}
            </section>;
    }

    return<section>{button}</section>;
}

buttonのonClickにhandleClickを渡すときにbindをしてthisを指定してやらないと、ちょっとはまります。

最後に、ReactDOMのrenderメソッドで、作成してReactComponentを表示します。

ReactDOM.render(
    <ToggleComponent text='Hello React!!' />,
    document.getElementById('content'));

実行してボタンを押すとHello React!!の文字が表示されたり消えたりします。

コード全体

app.tsx

/// <reference path="typings/tsd.d.ts" />interface ToggleComponentProperties {
    text:string;
}

interface ToggleComponentState {
    visible: boolean;
}

class ToggleComponent extends React.Component<ToggleComponentProperties, ToggleComponentState> {
    constructor(props: ToggleComponentProperties) {
        super(props);
        this.state = { visible: true } as ToggleComponentState;
    }

    handleClick(): boolean {
        this.setState({ visible: !this.state.visible } as ToggleComponentState);
        returnfalse;
    }

    render() {
        var header = <h1 className='header'>{this.props.text}</h1>;
        var button = <button onClick={this.handleClick.bind(this) }>Toggle!</button>;

        if (this.state.visible) {
            return<section>
                    {header}
                    {button}
                </section>;
        }

        return<section>{button}</section>;
    }
}

ReactDOM.render(
    <ToggleComponent text='Hello React!!' />,
    document.getElementById('content'));

index.html

<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8" /><title>TypeScript HTML App</title></head><body><divid="content"></div><scriptsrc="bower_components/react/react.min.js"></script><scriptsrc="bower_components/react/react-dom.min.js"></script><scriptsrc="bower_components/react/react-with-addons.min.js"></script><scriptsrc="app.js"></script></body></html>

Visual Studio上のTypeScript JSXを使ってReact.js「デフォルトのプロパティ」

$
0
0

JSXとかだとgetDefaultPropsというメソッドを定義しておくとプロパティのデフォルト値として使ってくれるんですがTypeScriptの場合違うらしい。

どうやるかというと、デフォルト値がほしいプロパティの定義に?をつけて必須じゃなくしてから、public static defaultPropsという感じのstaticなプロパティを定義するという感じでやるみたいです。こんな感じ。

/// <reference path="typings/tsd.d.ts" />interface ToggleComponentProperties {
    text?: string;
}

interface ToggleComponentState {
    visible: boolean;
}

class ToggleComponent extends React.Component<ToggleComponentProperties, ToggleComponentState> {
    publicstatic defaultProps: ToggleComponentProperties = { text: "Default!!" }

    constructor(props: ToggleComponentProperties) {
        super(props);
        this.state = { visible: true } as ToggleComponentState;
    }

    handleClick(): boolean {
        this.setState({ visible: !this.state.visible } as ToggleComponentState);
        returnfalse;
    }

    render() {
        var header = <h1 className='header'>{this.props.text}</h1>;
        var button = <button onClick={this.handleClick.bind(this) }>Toggle!</button>;

        if (this.state.visible) {
            return<section>
                    {header}
                    {button}
                </section>;
        }

        return<section>{button}</section>;
    }
}

ReactDOM.render(
    <ToggleComponent />,
    document.getElementById('content'));

Visual Studio上のTypeScript JSXを使ってReact.js「style属性をJSのオブジェクトで指定する」

$
0
0

ということができるみたいですね。

fontSize → font-sizeみたいな感じのルールでJSのオブジェクトのプロパティをCSSのstyleの名前に変換してくれるみたいです。こんな感じで

/// <reference path="typings/tsd.d.ts" />

var h2Style = {
    fontSize:40,
    color:'red',
    backgroundColor:'yellow'
};

ReactDOM.render(
    <h2 style={h2Style}>Hello world</h2>,
    document.getElementById('content'));

これで背景黄色で文字が赤のHello worldが表示されます。

Visual Studio上のTypeScript JSXを使ってReact.js「ReactのチュートリアルをTypeScriptでリライト」

$
0
0

なんとなくReactのチュートリアルをTypeScript JSXでこなせるような気がしてきたので、やってみたいと思います。

facebook.github.io

プロジェクトの初期設定とかは以下の記事を参照してください。

blog.okazuki.jp

最初のコンポーネント

最初はCommentBoxという名前でHello worldをしてるような感じなのでさくっといきましょう。

/// <reference path="typings/tsd.d.ts" />class CommentBox extends React.Component<any, any> {
    render() {
        return<div className="commentBox">
            Hello, world!I am a CommentBox.
            </div>;
    }
}

ReactDOM.render(
    <CommentBox />,
    document.getElementById("content"));

コンポーネントの組み立て

次は、コンポーネントを複数作って組み合わせるフェーズです。各コンポーネントの中身は、まだHello worldです。

/// <reference path="typings/tsd.d.ts" />class CommentList extends React.Component<any, any> {
    render() {
        return<div className="commentList">
                Hello, world! I am a CommentList.
            </div>;
    }
}

class CommentForm extends React.Component<any, any> {
    render() {
        return<div className="commentForm">
                Hello, world! I am a CommentForm.
            </div>;
    }
}

class CommentBox extends React.Component<any, any> {
    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList />
                <CommentForm />
            </div>;
    }
}

ReactDOM.render(
    <CommentBox />,
    document.getElementById("content"));

Props を使う

Commentを定義してpropsの使い方を示している箇所です。ただ、Commentっていうクラスを定義すると重複してる(何と?)と怒られたのでCommentItemという名前のクラスにしました。そこだけオリジナルチュートリアルと違います。

interface CommentItemProps extends React.Props<any> {
    author:string;
}

class CommentItem extends React.Component<CommentItemProps, any> {
    render() {
        return<div className="commentItem">
                <h2 className="commentAuthor">{this.props.author}</h2>
                {this.props.children}
            </div>;
    }
}

コンポーネントのプロパティ

先ほど定義したCommentItemをCommentListに埋め込んでいます。

class CommentList extends React.Component<any, any> {
    render() {
        return<div className="commentList">
                <CommentItem author="Pete Hunt">This is one comment</CommentItem>
                <CommentItem author="Jordan Walke">This is *another* comment</CommentItem>
            </div>;
    }
}

Markdown の追加

markedというライブラリを使うみたいなので、パッケージマネージャーコンソールに以下のコマンドを打ち込んでJSのファイルとd.ts(あってよかった)インストールして、ソリューションエクスプローラですべてのファイルを表示するにしてから、プロジェクトに取り込みます。

PM> tsd install marked -save
PM> bower install marked

index.htmlにmarkedのjsを読み込ませます。

<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8" /><title>React tutorial</title></head><body><divid="content"></div><scriptsrc="bower_components/jquery/dist/jquery.min.js"></script><scriptsrc="bower_components/react/react.js"></script><scriptsrc="bower_components/react/react-dom.min.js"></script><scriptsrc="bower_components/marked/marked.min.js"></script><scriptsrc="app.js"></script></body></html>

CommentItemをmarkedを使って書き直します。

class CommentItem extends React.Component<CommentItemProps, any> {
    render() {
        return<div className="commentItem">
                <h2 className="commentAuthor">{this.props.author}</h2>
                {marked(this.props.children.toString())}
            </div>;
    }
}

この状態では、サニタイズされてHTMLのタグが、そのままブラウザ上に表示されてしまうため、サニタイズしないように変更します。

class CommentItem extends React.Component<CommentItemProps, any> {
    render() {
        var rawMarkup = marked(this.props.children.toString());
        return<div className="commentItem">
                <h2 className="commentAuthor">{this.props.author}</h2>
                <span dangerouslySetInnerHTML={{ __html: rawMarkup }}></span>
            </div>;
    }
}

データモデルとの連携

まずは、データを用意します。インターフェースでタイプセーフに定義できるようにしてみましょう。

interface Data {
    author:string;
    text:string;
}

var data: Data[] = [
    { author: "Pete Hunt", text: "This is one comment" },
    { author: "Jordan Wakle", text: "This is *another* comment" }
];

そして、CommentBoxとCommentListがdataというプロパティを受け取るようになったので、変更します。

/// <reference path="typings/tsd.d.ts" />interface Data {
    author:string;
    text:string;
}

var data: Data[] = [
    { author: "Pete Hunt", text: "This is one comment" },
    { author: "Jordan Wakle", text: "This is *another* comment" }
];


interface CommentItemProps extends React.Props<any> {
    author:string;
}

class CommentItem extends React.Component<CommentItemProps, any> {
    render() {
        var rawMarkup = marked(this.props.children.toString());
        return<div className="commentItem">
                <h2 className="commentAuthor">{this.props.author}</h2>
                <span dangerouslySetInnerHTML={{ __html: rawMarkup }}></span>
            </div>;
    }
}

interface CommentListProps extends React.Props<any> {
    data: Data[];
}

class CommentList extends React.Component<CommentListProps, any> {
    render() {
        return<div className="commentList">
                <CommentItem author="Pete Hunt">This is one comment</CommentItem>
                <CommentItem author="Jordan Walke">This is *another* comment</CommentItem>
            </div>;
    }
}

class CommentForm extends React.Component<any, any> {
    render() {
        return<div className="commentForm">
                Hello, world! I am a CommentForm.
            </div>;
    }
}

interface CommentBoxProps extends React.Props<any> {
    data: Data[];
}

class CommentBox extends React.Component<CommentBoxProps, any> {
    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.props.data} />
                <CommentForm />
            </div>;
    }
}

ReactDOM.render(
    <CommentBox data={data} />,
    document.getElementById("content"));

そして、データを使って動的にレンダリングするようにします。

class CommentList extends React.Component<CommentListProps, any> {
    render() {
        var commentNodes = this.props.data.map(x => <CommentItem author={x.author}>{x.text}</CommentItem>);
        return<div className="commentList">
                {commentNodes}
            </div>;
    }
}

サーバからのデータの取得

CommentBoxのプロパティをurlにしてしまいます。

interface CommentBoxProps extends React.Props<any> {
    url:string;
}

class CommentBox extends React.Component<CommentBoxProps, any> {
    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.props.data} />
                <CommentForm />
            </div>;
    }
}

ReactDOM.render(
    <CommentBox url="api/comments.json" />,
    document.getElementById("content"));

この状態では、まだCommentBoxにコンパイルエラーがあります。なおしていきましょう。

Reactive state

CommentBoxにStateを定義します。インターフェースを新たに切って型引数に指定します。 そしてコンストラクタでStateの初期値を指定します。

interface CommentBoxProps extends React.Props<any> {
    url:string;
}

interface CommentBoxState {
    data: Data[];
}

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm />
            </div>;
    }
}

そして、データをサーバーから読み込むようにします。

interface CommentBoxProps extends React.Props<any> {
    url:string;
}

interface CommentBoxState {
    data: Data[];
}

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    componentDidMount() {
        $.ajax({
            url:this.props.url,
            dataType:"json",
            cache:false,
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm />
            </div>;
    }
}

続けて、コメントを2秒間隔で読み込む変更を加えます。

interface CommentBoxProps extends React.Props<any> {
    url:string;
    poolInterval: number;
}

interface CommentBoxState {
    data: Data[];
}

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    private loadCommentsFromServer() {
        $.ajax({
            url:this.props.url,
            dataType:"json",
            cache:false,
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
   }

    componentDidMount() {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer.bind(this), this.props.poolInterval);
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm />
            </div>;
    }
}

ReactDOM.render(
    <CommentBox url="api/comments.json" poolInterval={2000} />,
    document.getElementById("content"));

setIntervalの第一引数のメソッドを渡すときはbindでthisを固定してあげないとエラーになります。 これで、サーバーのデータを更新すると画面が2秒以内に更新されるようになりました。

新しいコメントの追加

refsの書き方が直感的でないことを除けば普通のTypeScriptでいけます。

class CommentForm extends React.Component<any, any> {
    private handleSubmit(e: React.FormEvent) {
        e.preventDefault();
        var author = (ReactDOM.findDOMNode(this.refs["author"]) as HTMLInputElement).value.trim();
        var text = (ReactDOM.findDOMNode(this.refs["text"]) as HTMLInputElement).value.trim();

        if (!text || !author) {
            return;
        }

        // TODO : サーバーにリクエストを送信
        (ReactDOM.findDOMNode(this.refs["author"]) as HTMLInputElement).value = "";
        (ReactDOM.findDOMNode(this.refs["text"]) as HTMLInputElement).value = "";
        return;
    }

    render() {
        return<form className="commentForm" onSubmit={this.handleSubmit.bind(this)}>
                <input type="text" placeholder="your name"ref="author" />
                <input type="text" placeholder="Say something..."ref="text" />
                <input type="submit"value="Post" />
            </form>;
    }
}

Props としてのコールバック

CommentFormのPropsにコールバックを定義して、それをhandleSubmitで呼び出します。

interface CommentFormProps extends React.Props<any> {
    onCommentSubmit: (data: Data) => void;
}

class CommentForm extends React.Component<CommentFormProps, any> {
    private handleSubmit(e: React.FormEvent) {
        e.preventDefault();
        var author = (ReactDOM.findDOMNode(this.refs["author"]) as HTMLInputElement).value.trim();
        var text = (ReactDOM.findDOMNode(this.refs["text"]) as HTMLInputElement).value.trim();

        if (!text || !author) {
            return;
        }

        this.props.onCommentSubmit({ author: author, text: text } as Data);
        (ReactDOM.findDOMNode(this.refs["author"]) as HTMLInputElement).value = "";
        (ReactDOM.findDOMNode(this.refs["text"]) as HTMLInputElement).value = "";
        return;
    }

    render() {
        return<form className="commentForm" onSubmit={this.handleSubmit.bind(this) }>
                <input type="text" placeholder="your name"ref="author" />
                <input type="text" placeholder="Say something..."ref="text" />
                <input type="submit"value="Post" />
            </form>;
    }
}

そして、CommentBoxで定義しているCommentFormに対してonCommentSubmitを指定します。

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    private loadCommentsFromServer() {
        $.ajax({
            url:this.props.url,
            dataType:"json",
            cache:false,
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
    }

    private handleCommentSubmit(comment: Data) {
        // TODO: サーバーに送信、リストをリフレッシュ
    }

    componentDidMount() {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer.bind(this), this.props.poolInterval);
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
            </div>;
    }
}

Web APIをたたいてコメントを登録するようにします…が、コメント投稿APIがないのでエラーになってしまいます。

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    private loadCommentsFromServer() {
        $.ajax({
            url:this.props.url,
            dataType:"json",
            cache:false,
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
    }

    private handleCommentSubmit(comment: Data) {
        $.ajax({
            url:this.props.url,
            dataType:'json',
            type:'POST',
            data: JSON.stringify(comment),
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
    }

    componentDidMount() {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer.bind(this), this.props.poolInterval);
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
            </div>;
    }
}

最適化: 先読み更新

残るは先読みですが、ここからは動作検証できてないので間違ってるかもしれません。

class CommentBox extends React.Component<CommentBoxProps, CommentBoxState> {
    constructor(props: CommentBoxProps) {
        super(props);
        this.state = { data: [] };
    }

    private loadCommentsFromServer() {
        $.ajax({
            url:this.props.url,
            dataType:"json",
            cache:false,
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => console.error(this.props.url, status, err.toString())).bind(this)
        });
    }

    private handleCommentSubmit(comment: Data) {
        var comments = this.state.data;
        var newComments = comments.concat([comment]);
        this.setState({ data: newComments } as CommentBoxState);
        $.ajax({
            url:this.props.url,
            dataType:'json',
            type:'POST',
            data: JSON.stringify(comment),
            success: (data => this.setState({ data: data } as CommentBoxState)).bind(this),
            error: ((xhr, status, err) => {
                this.setState({ data: comments } as CommentBoxState);
                console.error(this.props.url, status, err.toString());
            }).bind(this)
        });
    }

    componentDidMount() {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer.bind(this), this.props.poolInterval);
    }

    render() {
        return<div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
            </div>;
    }
}

これで一通りTypeScriptでReactのチュートリアルのコードを書き直したことになります。最後、動作確認できないのがつらいところですが、まぁ雰囲気がつかめたのでよしとしましょう。

それにしてもrefsの書き心地だけが果てしなく悪い…。

リポジトリ

一応GitHubに公開しておきます

github.com

TypeScript JSXでラジオボタンのリストを作る

$
0
0

onChangeで選択された値を渡してくれるコールバックと、defaultValueで、初期選択の値を設定できるみたいな感じです。 ラジオボタンのリストの要素はoptionタグで指定できる雰囲気でいってみましょう。

まず、プロパティを定義します。

interface RadioListProps extends React.Props<{}> {
    defaultValue?: string;
    onChange?: (value: string) => void;
}

ステートには、選択中の値を持たせることにします。

interface RadioListState {
    value:string;
}

あとはrenderで割と頑張る。ポイントは、React.Children.forEachで子要素をループ回してるところです。

class RadioList extends React.Component<RadioListProps, RadioListState> {
    constructor(props: RadioListProps) {
        super(props);
        this.state = { value: this.props.defaultValue } as RadioListState;
    }

    private handleChanged(value: string, e: React.SyntheticEvent) {
        this.props.onChange(value);
        this.setState({ value: value } as RadioListState);
    }

    render() {
        var radios = new Array<React.ReactElement<any>>();
        React.Children.forEach(this.props.children, (option: React.ReactElement<{ value: string; label: string; }>, index) => {
            radios.push(<div>
                    <label>
                        <input
                            type="radio"value={option.props.value}
                            checked={this.state.value === option.props.value}
                            onChange={this.handleChanged.bind(this, option.props.value) } />
                        {option.props.label}
                    </label>
                </div>);
        });

        return<span>{radios}</span>;
    }
}

こんな感じに使えます。

ReactDOM.render(<RadioList onChange={x => console.log(x) } defaultValue="B">
        <option value="A" label="Aです" />
        <option value="B" label="Bです" />
        <option value="C" label="Cです" />
    </RadioList>,
    document.getElementById("content"));

Visual Studio 2015 + ReactのHello world環境作り

$
0
0

はじめに

Reactをやりはじめてimport なにがしみたいなモジュールを取り込むやり方に憧れて、そういう環境作れないかと試行錯誤した結果です。gruntとかは毎回準備するにはヘビーなので入れてません。

ということで備忘録開始。

1回だけやればいい作業

node.js関連

node.jsを入れます。

コンソールで以下のコマンドをたたいて必要なものを入れておきます。

node install -g tsd
node install -g browserify

プロジェクト作成単位にやる作業

プロジェクトの作成と設定

TypeScriptを使用したHTMLアプリケーションを作成します。app.tsは邪魔なので消しておきます。

プロジェクトのプロパティでTypeScriptビルドのTSXファイルでのJSXコンパイルを応答に変更して、モジュールシステムをCommonJSにしておきます。

ライブラリのインストール

パッケージマネージャーコンソールを開いて以下のコマンドを打ち込んで必要なライブラリを突っ込みます。

PM> cd プロジェクト名
PM> npm install react
PM> npm install react-dom

次にTypeScriptの型定義ファイルを突っ込みます。

PM> tsd install react
PM> tsd install react-dom

ビルドイベントの設定

TypeScriptがコンパイルした結果がそのままブラウザで読み込める形になってないのでbrowserifyを使って変換します。プロジェクトのプロパティのビルドイベントでビルド後のイベントのコマンドラインに以下の記述を追加します。

cd $(ProjectDir)
browserify scripts/app.js -o bundle.js

エントリポイントになるtsxの作成

scripts/app.tsxという名前で作っておきます。

その他tsxの作成

今回はHello worldを表示するだけのHelloWorldというコンポーネントをscripts/helloworld.tsxに作ります。

import * as React from 'react';

export class HelloWorld extends React.Component<{}, {}> {
    render() {
        return<h1>Hello world</h1>;
    }
}

app.tsxにこれを取り込んでレンダリングする処理を書きます。

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as Components from './helloworld';

ReactDOM.render(<Components.HelloWorld />,
    document.getElementById('content'));

HTMLの編集

ビルドするとbundle.jsができてるはずなので、それを取り込みます。

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8" /><title>TypeScript HTML App</title><linkrel="stylesheet"href="app.css"type="text/css" /></head><body><h1>TypeScript HTML App</h1><divid="content"></div><scriptsrc="bundle.js"></script></body></html>

実行すると以下のようにHello worldが表示されたら成功です。

f:id:okazuki:20151228125217p:plain

TypeScript JSXでReactRouter

$
0
0

react-routerを使うまでのメモです。ちなみに、開始地点は、以下の記事のbrowserifyを使うようにしたプロジェクトです。

blog.okazuki.jp

型定義ファイルをダウンロード

以下のコマンドをうって型定義ファイルをダウンロードしておきます。

tsd install -save react react-dom react-router

ライブラリのダウンロード

以下のコマンドを打ち込んで必要なライブラリをダウンロードします。

npm install react react-dom react-router history

ReactRouterを使う

ReactRouterを使うには、以下のような感じでいきます。Router, Route, IndexRoute, Linkあたりをimportで取り込んでおけばよさげ。

Page1とPage2というコンポーネントと、Appというコンポーネントを作ってみました。Appのthis.props.childrenにPage1とPage2を埋め込もうという感じです。

Linkでページのパスを指定してます。

/// <reference path="../typings/tsd.d.ts" />
import * as React from 'react';
import {Router, Route, IndexRoute, Link} from 'react-router';

export class Page1 extends React.Component<{}, {}> {
    render() {
        return<h1>Page1</h1>;
    }
}

export class Page2 extends React.Component<{}, {}> {
    render() {
        return<h1>Page2</h1>;
    }
}

export class App extends React.Component<React.Props<{}>, {}> {
    render() {
        return<div>
                <header>
                    <h1>React router sample</h1>
                    <Link to="/">Page1</Link>
                    <Link to="/Page2">Page2</Link>
                </header>
                <div>
                    {this.props.children}
                </div>
            </div>;
    }
}

次に、Routerを組み立てます。 Routerタグの中にRouteを仕込む感じです。pathでURLのパスを指定してcomponentで対応するコンポーネントを定義します。

/// <reference path="../typings/tsd.d.ts" />
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as Components from './components';
import {Router, Route, IndexRoute, Link} from 'react-router';

var route = <Router>
        <Route path="/" component={Components.App}>
            <IndexRoute component={Components.Page1} />
            <Route path="/Page2" component={Components.Page2} />
        </Route>
    </Router>;

ReactDOM.render(route,
    document.getElementById('content'));

HTMLはこんな感じ。

<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8" /><title>TypeScript HTML App</title><linkrel="stylesheet"href="app.css"type="text/css" /></head><body><divid="content"></div><scriptsrc="bundle.js"></script></body></html>

実行すると以下のような感じになります。

f:id:okazuki:20151228154951p:plain

f:id:okazuki:20151228154957p:plain

とっかかりがわかったので、あとはこのページをみて勉強しよう。

beck23.hatenablog.com

Viewing all 1388 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>