==React==
[[Node.js]]
{{amazon|4873117887}}
#[http://typea.info/blg/glob/2017/08/react-react-router-redux-saga.html React の単純なサンプルに React Router を組み込んだものに Redux-Saga を適用する]
#[http://typea.info/blg/glob/2017/08/react-react-router-redux-saga-ajax.html React の単純なサンプルに React Router を組み込んだものに Redux-Saga を適用したものからAjax通信を組み込む]
#[http://typea.info/blg/glob/2017/08/react-react-router-redux-saga-superagent-bootstrap.html React環境->>React Router->>Redux-Saga->>SuperAgent に Bootstrapを適用する]
==導入==
*Reactはバックエンドロジックやデータベースを持たないが、使いたいものを使えばよい。
*Babelやwebpackのようなビルドツールも設定なしに利用できる。
PS C:\workspaces\vscode\reactlesson> > create-react-app react-lesson
Creating a new React app in C:\workspaces\vscode\reactlesson\react-lesson.
:
*アプリケーションが作成されたら実行
PS C:\workspaces\vscode\reactlesson> > cd react-lesson PS C:\workspaces\vscode\reactlesson> > npm start
*実行された
[[File:1086_react01.jpg]]
import './App.css';
const element = (<<h1>>Hello,world<</h1>>);
class App extends Component {
render() {
==クイックスタート==
===JSX===
const element = <<h1>>Hello,world!<</h1>>;
*文字列でも、HTMLでもなく、JSX
*JavaScriptの拡張文法
===JSX表現===
*どのようなJavaScriptの表現も、中括弧で囲むことでJSXに埋め込むことができる
*const element = (<<h1>>Hello, {formatName(user)}!<</h1>>);
<<!DOCTYPE html>> <<html>> <<head>> <<meta charset="UTF-8" />> <<title>>Hello World<</title>> <<script src="https://unpkg.com/react@latest/dist/react.js"><></script>> <<script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"><></script>> <<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"><></script>> <</head>> <<body>> <<div id="root"><></div>> <<script type="text/babel">>
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {firstName:'Hiroto', lastName:'Yagi'};
const element = (<<h1>>Hello, {formatName(user)}!<</h1>>);
ReactDOM.render(
element,
document.getElementById('root')
);
<</script>> <</body>> <</html>>
*コンパイルされたJSXは通常のJavaScriptオブジェクト
function greeting(user) {
var now = new Date();
if (5 <<= now.getHours() && now.getHours() <<= 12) { return <<h1> > Good morning {user.firstName + ' ' + user.lastName}.<</h1>>;
} else {
return <<h1> > Hello {user.firstName + ' ' + user.lastName}.<</h1>>;
}
}
====属性に利用====
*中括弧を利用して、二重引用符なし(使用すると文字列リテラルとして扱われる)で直接利用できる。
<<div id="root"><></div>> <<script type="text/babel">>
function greeting(user) {
var now = new Date();
if (5 <<= now.getHours() && now.getHours() <<= 12) { return <<h1> > Good morning <<a href={user.webpageUrl}>>{user.firstName + ' ' + user.lastName}<</a>>.<</h1>>;
} else {
return <<h1> > Hello <<a href={user.webpageUrl}>>{user.firstName + ' ' + user.lastName}<</a>>.<</h1>>;
}
}
const user = {firstName:'Hiroto', lastName:'Yagi', webpageUrl:'http://typea.info'};
const element = (<<h1>>{greeting(user)}<</h1>>);
ReactDOM.render(
element,
[[File:1089_react04.jpg]]
====子要素====
*子要素がない場合、XML同様 /> > で閉じる
*子要素を含む
*JSXはHTMLよりJavaScriptにより近い。ReactDOMはキャメルケースプロパティ(HTMLでは、class がclassName、HTMLではtabindexがtabIndexなど)を持つ
function greeting(user) {
return <<div>> <<h1> > Good morning {user.firstName + ' ' + user.lastName}.<</h1>> <<h2><><a href={user.webpageUrl}>>webpage<</a><></h2>> <</div>>
;
}
*XSS攻撃の予防を助ける
function greeting(user, title) {
return <<div>> <<h1>>{title}<</h1>> <<h2> > Good morning {user.firstName + ' ' + user.lastName}.<</h2>> <<h2><><a href={user.webpageUrl}>>webpage<</a><></h2>> <</div>>
;
}
const title = "<<input type='button'>>";
const user = {firstName:'Hiroto', lastName:'Yagi', webpageUrl:'http://typea.info'};
const element = (greeting(user,title));
*Babelは、React.createElement()を呼び出しコンパイルを行う
*以下の2つは同じ意味
const element1 = (<<h1 className='greeting'>>hello<</h1>>);
const element2 = React.createElement('h1',{className:'greeting'},'hello');
*React.createElement()は、バグを防ぐ手助けをするが、本質的には以下のようなオブジェクトを生成する
*HTMLどこかに記述する、以下のDIV要素をroot DOM ノードと呼ぶ
*React DOM が管理するすべてが、この要素の中にある
<<div id="root"><></div>>
====React要素の更新====
*React要素は不変。一旦生成したら、子要素、属性などは変更できない。
*UIを更新するには、新しい要素を作成し、ReactDOM.rendar()に引き渡す。
function tick() {
const element = (<<div>>{(new Date()).toLocaleTimeString()}<</div>>);
ReactDOM.render(
element,
setInterval(tick,1000);
[[File:1092_react07.jpg]]
<<blockquote>>通常のReact アプリケーションでは、ReactDOM.rendar()は 一度しか呼びださない。<</blockquote>>
====Reactは必要なもののみ更新する====
*React DOMは、要素および子要素を前の状態と比較し、DOMの更新が必要な個所にのみ適用する。
*ブラウザツールで、上記のソースコードを確認する
[[File:1093_react08.jpg]]
<<blockquote>>毎tick()の呼び出しで、すべてのUIツリーを生成するよう記述してるが、変更が発生したテキストノードのみReact DOMにより更新されている。<</blockquote>>
===コンポーネントと Props===
*コンポーネントはUIを独立し再利用可能な部分に分割する。
*このようなコンポーネントを"functional"と呼ぶ。
function Welcome(props) {
return <<h1>>Hello,{props.name}<</h1>>;
}
*ES6のクラスをコンポーネントの定義として利用できる
class Welcome extends React.Component {
render() {
return <<h1>>Hello,{props.name}<</h1>>;
}
}
*Elementは、ユーザー定義コンポーネントも表すことができる
function Welcome(props) {
return <<h1>>Hello,{props.name}<</h1>>;
}
const element = <<Welcome name='Hiroto' />>
ReactDOM.render(
element,
*Reactがユーザー定義コンポーネントを表示するときに、JSXの属性からコンポーネントへ"props"としてオブジェクトが渡される。
<<blockquote>>コンポーネント名はいつも大文字から始める。DOM タグは、<<div /> > だが、<<Welcome /> > はコンポーネントを表現する。Welcomeがスコープに存在すること。<</blockquote>>
====コンポーネントの構成====
*コンポーネントはその出力において、他のコンポーネントに影響を与えることができる
*Reactでこれらは、一般にコンポーネントで表現される
function Welcome(props) {
return <<h1>>Hello,{props.name}<</h1>>;
}
function App() {
return ( <<div>> <<Welcome name="Yagi"/>> <<Welcome name="Kaela"/>> <<Welcome name="Hiroto"/>> <</div> > );
}
ReactDOM.render(
<<App />>,
document.getElementById('root')
);
*一般的に新しいReactアプリケーションは、一つのAppコンポーネントを最上位に持つ。既存のアプリケーションにReactを統合する場合は、小さなコンポーネント、例えばButtonなど、ボトムアップから開始し、徐々にView階層の最上位にいたる。
<<blockquote>>コンポーネントは、一つのroot要素を返さなければならない。上記で、Welcome要素を div に含めたのはこのため<</blockquote>>
====Propsは読み取り専用====
**Reactはかなりフレキシブルだが、1つ厳格なルールがある。function だろうと class だろうとコンポーネントはpropsを編集できない。
function Clock(props) {
return (
<<div>> <<h2>>{props.date.toLocaleTimeString()}<</h2>> <</div>>
);
}
function tick() {
ReactDOM.render(
<<Clock date={new Date()} />>,
document.getElementById('root')
);
render() {
return (
<<div>> <<h2>>{this.props.date.toLocaleTimeString()}<</h2>> <</div>>
);
}
#render()メソッドの中の、this.props.date を this.state.date に変更
#クラスにコンストラクタを追加し、this.stateの初期状態を記述する
#<<Clock date={new Date()} /> > から dateを削除
class Clock extends React.Component {
constructor(props) {
render() {
return (
<<div>> <<h2>>{this.state.date.toLocaleTimeString()}<</h2>> <</div>>
);
}
componentDidMount() {
this.timerID = setInterval(
() => > this.tick(),1000
);
}
render() {
return (
<<div>> <<h2>>{this.state.date.toLocaleTimeString()}<</h2>> <</div>>
);
}
}
ReactDOM.render(
<<Clock />>,
document.getElementById('root')
);
// 正しい
this.setSatate({comment:'Hello'});
<<blockquote>>this.stateを割り当てることができるのは、コンストラクタのみ<</blockquote>>
=====stateは、非同期に更新される=====
*Reactは、複数のsetState() をパフォーマンスのためにまとめて一度に処理する。
*関数は、ひとつ前の状態(state)を最初の引数として、更新時のpropsを2つ目の引数として取る
// 正しい
this.setState((prevState, props) => > ({
counter: prevState.counter + prpos.increament
}));
*これらを別のsetState() で更新
componentDidMount() {
fetchPosts().then(response => > { this.setState({ posts: response.posts }); }); fetchComments().then(response => > { this.setState({ commentss: response.comments }); });
}
*マージはシャローなので、setState({comment}) は、this.state.posts を損なわずに、this.state.comments を置き換える
*状態はしばしばローカルから呼び出されるかカプセル化されていて、他からアクセスできない。
*コンポーネントは、自身の状態をpropsを通して子コンポーネントに渡す
<<h2>>It is {this.state.date.toLocalTimeString()}.<</h2>>
*これは、ユーザー定義コンポーネントでも動作する
<<FormattedDate date={this.state.date} />>
*FormattedDateコンポーネントは、propsにdateを受け取り、Clockの状態が何かは知らない。
function FormatterDate(props) {
return <<h2>>It is {props.date.toLocaleTimeString()}.<</h2>>;
}
*これは一般的に、トップダウン、もしくは、ユニディレクショナル データフローという
=====HTML=====
<<button onclick="test()" />>
=====React=====
<<button onClick={test} />>
*もう一つの違いは、Reactでは、falseを返しデフォルトのふるまいを防止することができない。
=====HTMLでデフォルトの新しいページを開くリンクのふるまいを防止する=====
<<a href="#" onclick="console.log('hoge'); return false" >>Click<</a>>
=====React=====
function ActionLink() {
}
return (
<<a href="#" onClick={handleClick} >>Click<</a>>
)
}
}
handleClick() {
this.setState(prevStae => > ({
isToggleOn: !prevStae.isToggleOn
}));
render() {
return (
<<button onClick={this.handleClick} >>
{this.state.isToggleOn?"ON":"OFF"}
<</button>>
);
}
}
ReactDOM.render(
<<Toggle />>,
document.getElementById('root')
);
*isLoggedIn により、異なるレンダリングがなされる。
function UserGreeting(props){
return <<h1>>Welcome to back!<</h1>>
}
function GuestGreeting(props) {
return <<h1>>Please Sign up!<</h1>>
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <<UserGreeting />>
} else {
return <<GuestGreeting />>
}
}
ReactDOM.render(
<<Greeting isLoggedIn={true} />>,
document.getElementById('root')
);
function LoginButton(props) {
return (
<<button onClick={props.onClick} >>
Login
<</button>>
);
}
function LogoutButton(props) {
return (
<<button onClick={props.onClick} >>
Logout
<</button>>
);
}
let button = null;
if (isLoggedIn) {
button = <<LogoutButton onClick={this.handleLogoutClick} />>
} else {
button = <<LoginButton onClick={this.handleLoginClick} />>
}
return (<<div>>{button}<</div>>);
}
}
ReactDOM.render(
<<LoginControl />>,
document.getElementById('root')
);
const unreadMessages = props.unreadMessages;
return (
<<div>> <<h1>>Hello!<</h1>> {unreadMessages.length > > 0 && <<h2>>
You have {unreadMessages.length} unread messages.
<</h2>>
}
<</div>>
);
}
const messages = ['message1','message2'];
ReactDOM.render(
<<MailBox unreadMessages={messages} />>,
document.getElementById('root')
);
*条件 ? true : false
function UserGreeting(props){
return <<h1>>Welcome to back!<</h1>>
}
function GuestGreeting(props) {
return <<h1>>Please Sign up!<</h1>>
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
return (
<<div>>
{isLoggedIn ? (
<<UserGreeting />>
) : (
<<GuestGreeting />>
)
}
<</div>>
);
}
ReactDOM.render(
<<Greeting isLoggedIn={false} />>,
document.getElementById('root')
);
}
return (
<<div>>Warning!<</div>>
);
}
}
handleToggleClick() {
this.setState(prevState =>>({showWarning : !prevState.showWarning}));
}
render() {
return (
<<div>> <<WarningBanner warn={this.state.showWarning} />> <<button onClick={this.handleToggleClick}>>
{this.state.showWarning ? 'Hide' : 'Show'}
<</button>> <</div>>
);
}
ReactDOM.render(
<<Page />>,
document.getElementById('root')
);
=====JavaScriptでリストの変換には、map()を利用する=====
const numbers = [1,2,3,4,5];
const doubled = numbers.map((number)=> > number * 2);
console.log(doubled)
> > [2, 4, 6, 8, 10]
*React では同様に配列をリストに変換する
====複数のコンポーネントをレンダリングする====
const fruits = ['apple','orange','grape','banana'];
const listItems = fruits.map((fruit) => <> <li>>{fruit}<</li>>);
ReactDOM.render(
<<ol>>{listItems}<</ol>>,
document.getElementById('root')
);
function FruitList(props) {
const fruits = props.fruits;
const listItems = fruits.map((fruit) => <> <li key={fruit}>>{fruit}<</li>>);
return (
<<ol>>{listItems}<</ol> >
);
}
const fruits = ['apple','orange','grape','banana'];
ReactDOM.render(
<<FruitList fruits={fruits}/>>,
document.getElementById('root')
);
*兄弟間で同じKeyを持つとエラーとなる
function ListItem(props) {
// この<<li> > にKeyを設定するのは、ここがルートになるため間違い return <<li>>{props.value}<</li>>
}
function FruitList(props) {
const fruits = props.fruits;
// <<ListItem>>の配列となるため、ここでKeyを指定するのが正しい const listItems = fruits.map((fruit) => <> <ListItem key={fruit} value={fruit} />>);
return (
<<ol>>{listItems}<</ol> >
);
}
const fruits = ['apple','orange','grape','banana'];
ReactDOM.render(
<<FruitList fruits={fruits}/>>,
document.getElementById('root')
);
*中括弧のインラインにmap()を置くことができる
function ListItem(props) {
return <<li>>{props.value}<</li>>
}
function FruitList(props) {
const fruits = props.fruits;
return (
<<ol>>{fruits.map((fruit) => <> <ListItem key={fruit} value={fruit} />>)}<</ol> >
);
const fruits = ['apple','orange','grape','banana'];
ReactDOM.render(
<<FruitList fruits={fruits}/>>,
document.getElementById('root')
);
render() {
return(
<<form onSubmit={this.handleSubmit}>> <<label>>Name: <<input type="text" value={this.state.value} onChange={this.handleChange} /><></label>> <<input type="submit" value="Submit"/>> <</form>>
);
}
}
ReactDOM.render(
<<NameForm />>,
document.getElementById('root')
);
render(){
return(
<<label>>Select: <<select value={this.state.value} onChange={this.handleChange}>> <<option value="grapefruit">>Grape Fruit<</option>> <<option value="lime">>Lime<</option>> <<option value="coconuts">>Coconuts<</option>> <<option value="mango">>Mango<</option>> <</select>> <</label>>
);
}
render(){
return (
<<label>>Textarea:<<textarea value={this.state.value} onChange={this.handleChange} /><></label>>
);
}
}
ReactDOM.render(
<<div>> <<SelectParts />> <<TextareaParts />> <</div>>,
document.getElementById('root')
);
render(){
return (
<<form>> <<label>>Is going:<<input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange}/><></label>> <<br />> <<label>>Number of guests:<<input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange}/><></label>> <</form>>
);
}
}
ReactDOM.render(
<<div>> <<Reservation />> <</div>>,
document.getElementById('root')
);
=====水が沸騰しているかどうかの計算を行う。=====
function BoilingVerdict(props) {
if (props.celsius >>= 100) { return <<p>>The water would boil.<</p>>;
}
return <<p>>The water would not boil.<</p>>
}
class Calculator extends React.Component {
const temprature = this.state.temprature;
return (
<<fieldset>> <<legend>>Enter temprature in Celsius:<</legend>> <<input value={temprature} onChange={this.handleChange} />> <<BoilingVerdict celsius={parseFloat(temprature)} /> > <</fieldset>>
);
}
}
ReactDOM.render(
<<div>> <<Calculator />> <</div>>,
document.getElementById('root')
);
};
function BoilingVerdict(props) {
if (props.celsius >>= 100) { return <<p>>The water would boil.<</p>>;
}
return <<p>>The water would not boil.<</p>>
}
function toCelsius(fahrenheit) {
const scale = this.props.scale;
return (
<<fieldset>> <<legend>>Enter temprature in {scaleName[scale]}:<</legend>> <<input value={temprature} onChange={this.handleChange} />> <<BoilingVerdict celsius={parseFloat(temprature)} /> > <</fieldset>>
);
}
const fahrenheit = scale == 'c' ? tryConvert(temprature, toFahrenheit) : temprature;
return (
<<div>> <<TempratureInput scale="c" temprature={celsius} onTempratureChange={this.handleCelsiumChange} />> <<TempratureInput scale="f" temprature={fahrenheit} onTempratureChange={this.handlFahrenheitChange}/>> <<BoilingVerdict celsius={parseFloat(celsius)} />> <</div>>
);
}
}
ReactDOM.render(
<<div>> <<Calculator />> <</div>>,
document.getElementById('root')
);
*これは、特にサイドバーやダイアログなどボックスを表現するコンポーネント共通
*このようなコンポーネントには特別な子供propを使用し直接その要素に出力する
*<<FancyBorder>>の内側に何があっても、JSXタグはFancyBorderコンポーネントをprops.children として経由する <<style type="text/css">>
div.FancyBorder {
border : 2px dashed;
border-color: blue;
}
<</style>>
function FancyBorder(props) {
return (
<<div className={'FancyBorder FancyBorder-' + props.color}>>
{props.children}
<</div>>
);
}
function WelcomDialog() {
return (
<<FancyBorder color="blue">> <<h1 className="Dialog-title">>Welcome<</h1>> <</FancyBorder>>
);
}
ReactDOM.render(
<<div>> <<WelcomDialog />> <</div>>,
document.getElementById('root')
);
function Dialog(props) {
return (
<<FancyBorder color="blue">> <<h1 className="Dialog-title">>{props.title}<</h1>> <</FancyBorder>>
);
}
function FancyBorder(props) {
return (
<<div className={'FancyBorder FancyBorder-' + props.color}>>
{props.children}
<</div>>
);
}
function WelcomDialog() {
return (
<<Dialog title="Welcome" />>
);
}