Skip to content

Instantly share code, notes, and snippets.

@kenfdev
Last active October 26, 2021 00:21
Show Gist options
  • Save kenfdev/fe61c4fb03eec1ca6ff4 to your computer and use it in GitHub Desktop.
Save kenfdev/fe61c4fb03eec1ca6ff4 to your computer and use it in GitHub Desktop.
React
// カスタム属性をそのままJSXで書くとコンパイル時に消えてしまう
React.render(
<h1 custom-attribute='foo'>いち・にぃ</h1>,
document.getElementById('example')
);
// 出力
// <div id="example">
// <h1 data-reactid=".0">いち・にぃ</h1>
// </div>
// カスタム属性には必ずdataをprefixとして記述する必要がある
React.render(
<h1 data-custom-attribute='foo'>いち・にぃ</h1>,
document.getElementById('example')
);
// 出力
// <div id="example">
// <h1 data-custom-attribute="foo" data-reactid=".0">いち・にぃ</h1>
// </div>
// Web Accessibility属性であるariaで始まる属性は正常に出力されます
React.render(
<div aria-hidden={true} />,
document.getElementById('example')
);
// 出力
// <div id="example">
// <div aria-hidden="true" data-reactid=".0"></div>
// </div>
var Stock = React.createClass({
render: function() {
// 無駄にスタイルを適用してみる
var pStyle = {
color: 'red'
};
// StockListコンポーネントのstockNodesをthis.props.childrenでアクセス
var tags = React.Children.map(this.props.children, function(child) {
return <p style={pStyle}>{child}</p>
});
return (
<div>
<h1>
{this.props.title}
</h1>
<h2>
{this.props.author}
</h2>
{tags}
</div>
);
}
});
var StockList = React.createClass({
render: function() {
var stockNodes = this.props.data.map(function (stock) {
console.log('stock',stock);
return (
<Stock author={stock.user.id} title={stock.title}>
{stock.tags.map(function(tag){ return tag.name})}
</Stock>
);
});
return (
<div className="stockList">
{stockNodes}
</div>
);
}
});
var StockBox = React.createClass({
loadStocksFromServer: function() {
// ajaxでAPIを叩く
$.ajax({
// とりあえずちょっと取得してみたいだけなので3ストック限定で取得
url: 'https://qiita.com/api/v2/users/' + this.props.userName + '/stocks?page=1&per_page=3',
dataType: 'json',
success: function(data) {
// 取得に成功したらstateに反映
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.username, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
// コンポーネントがマウントされたら読み込み開始
this.loadStocksFromServer();
},
render: function() {
return (
<div className="stockBox">
<h1>Stocks</h1>
<StockList data={this.state.data} />
</div>
);
}
});
// StockBoxコンポーネントのuserNameを適当に変えることで
// 誰かしらのストックをとってくる
React.render(
<StockBox userName="kenfdev" />,
document.getElementById('content')
);
// this creates 'load', 'load.completed' and 'load.failed'
var Actions = Reflux.createActions({
load: { children: ['completed', 'failed'] },
});
// called before the 'load' action emits an event
Actions.load.preEmit = function () {
console.log(arguments);
};
// called after preEmit
Actions.load.shouldEmit = function (isFail) {
console.log('isFail:' + isFail);
return !isFail;
};
// when 'load' is triggered, call async operation and trigger related actions
Actions.load.listen(function (isFail) {
// By default, the listener is bound to the action
// so we can access child actions using 'this'
dummyPromiseCall(isFail).then(this.completed).catch(this.failed);
});
// a dummy promise call which resolves or rejects 1000ms later
var dummyPromiseCall = function (isFail) {
var deferred = Q.defer();
// fake asynchronous call
setTimeout(function () {
if (isFail) {
deferred.reject('Failed!');
} else {
deferred.resolve('Success!');
}
}, 1000);
return deferred.promise;
};
/*********Actions*********/
// this creates 'load', 'load.completed' and 'load.failed'
var Actions = Reflux.createActions({
load: { children: ['completed', 'failed'] },
});
// called before the 'load' action emits an event
Actions.load.preEmit = function () {
console.log(arguments);
};
// called after preEmit
Actions.load.shouldEmit = function (isFail) {
console.log('isFail:' + isFail);
return !isFail;
};
// when 'load' is triggered, call async operation and trigger related actions
Actions.load.listen(function (isFail) {
// By default, the listener is bound to the action
// so we can access child actions using 'this'
dummyPromiseCall(isFail).then(this.completed).catch(this.failed);
});
// `promise` style
// Actions.load.listen(function(isFail) {
// Actions.load.promise(dummyPromiseCall(isFail));
// });
// `listenAndPromise` style
// Actions.load.listenAndPromise(dummyPromiseCall);
/*********Actions*********/
/*********Store*********/
// Creating Data Stores(https://github.com/reflux/refluxjs#creating-data-stores)
var Store = Reflux.createStore({
// Listening to many Actions at once(https://github.com/reflux/refluxjs#listening-to-many-actions-at-once)
listenables: [Actions],
// called when 'load' triggers
onLoad: function () {
this.trigger('Loading...');
},
// Listenabled and asynchronous actions(https://github.com/reflux/refluxjs#listenables-and-asynchronous-actions)
// called when 'load.completed' triggers
onLoadCompleted: function (msg) {
this.trigger(msg);
},
// called when 'load.failed' triggers
onLoadFailed: function (msg) {
this.trigger(msg);
},
});
/*********Store*********/
/*********React Component*********/
var Hello = React.createClass({
// Convenience mixin for React(https://github.com/reflux/refluxjs#convenience-mixin-for-react)
// https://github.com/reflux/refluxjs#using-refluxlistento
mixins: [
Reflux.listenTo(Store, 'onLoad'),
Reflux.listenTo(Store, 'onLoadComplete'),
Reflux.listenTo(Store, 'onLoadFailed'),
],
// initialize this.state.message
getInitialState: function () {
return {
message: 'initial message',
};
},
handleLoadSuccess: function () {
// trigger the 'load' Action
Actions.load(false /*isFail*/);
},
handleLoadFail: function () {
// trigger the 'load' Action
Actions.load(true /*isFail*/);
},
// called when the Store fires 'onLoad'
onLoad: function (msg) {
this.setState({ message: msg });
},
// called when the Store fires 'onLoadComplete'
onLoadComplete: function (msg) {
this.setState({ message: msg });
},
// called when the Store fires 'onLoadFailed'
onLoadFailed: function (msg) {
this.setState({ message: msg });
},
render: function () {
return (
<div>
<button onClick={this.handleLoadSuccess}>Load Success</button>
<button onClick={this.handleLoadFail}>Load Fail</button>
<div>{this.state.message}</div>
</div>
);
},
});
React.render(<Hello />, document.body);
/*********React Component*********/
var ExampleApplication = React.createClass({
render: function () {
// ここでthis.props.nameを無理矢理変更
// 出力結果は「mutated name!」になります
return <p>{(this.props.name = 'mutated name!')}</p>;
},
});
var start = new Date().getTime();
React.render(
<ExampleApplication name="kenfdev" />,
document.getElementById('container')
);
var MyComponent = React.createClass({
// static method
statics: {
message: 'Hello',
staticMethod: function () {
// < v0.13rc:thisはコンポーネント(MyComponent)に自動バインド
// v0.13rc:thisはコンポーネントに自動バインドされない
return this.message;
},
},
render: function () {},
});
// messageを持つオブジェクトを準備
var caller = {
message: 'Hey!',
};
// Greetings component
var Greetings = React.createClass({
render: function () {
// < v0.13rc:staticMethodはMyCompoentにバインドされているので出力結果は「Hello」
// v0.13rc:staticMethodはMyComponentにバインドされておらず、呼び出し元をcallerに明示的に設定している為出力結果は「Hey!」
return <div>{MyComponent.staticMethod.call(caller)}</div>;
},
});
React.render(<Greetings></Greetings>, document.body);
// Helloコンポーネントを2秒後にアンマウントさせる為の設定
var timeoutBeforeUnmount = 2000;
// The Hello Component
var Hello = React.createClass({
getInitialState: function () {
return { message: 'World' };
},
componentDidMount: function () {
var self = this;
setTimeout(
function () {
// setStateを呼び出す
self.setState({ message: 'again to the World' });
},
// Helloコンポーネントの1秒後にsetStateを発火させる
timeoutBeforeUnmount + 1000
);
},
render: function () {
return <div>Hello {this.state.message}</div>;
},
});
React.render(<Hello />, document.getElementById('container'));
// Helloコンポーネントを2秒後にアンマウント
setTimeout(function () {
React.unmountComponentAtNode(document.getElementById('container'));
}, timeoutBeforeUnmount);
# react.js < v0.13rc 例外発生
Uncaught Error: Invariant Violation: replaceState(...): Can only update a mounted or mounting component.
# v0.13rc <= react.js 警告止まり
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
var Hello = React.createClass({
getInitialState: function () {
return { count: 0 };
},
handleClick: function (e) {
// countをインクリメント
this.setState({ count: this.state.count + 1 });
// 同一関数内で再度インクリメント
// 2になってくれるかと思いきや、this.state.countは即時反映されるわけではないので、上のsetStateも下のsetStateも「0 + 1」を実行してしまい、結果は「1」になってしまう。
this.setState({ count: this.state.count + 1 });
},
render: function () {
return (
<div onClick={this.handleClick}>
Click Me and the Count will try to increment by 2 but will fail!
<br />
Count: {this.state.count}
</div>
);
},
});
React.render(<Hello />, document.body);
this.setState({ count: this.state.count + 1 });
// 'this._pendingState'なら即時反映された値にアクセスできる
this.setState({ count: this._pendingState.count + 1 });
var Hello = React.createClass({
getInitialState: function () {
return { count: 0 };
},
handleClick: function (e) {
// setStateに関数を渡すことが可能。この計算は「0 + 1」となる
this.setState(function (state, props) {
return { count: state.count + 1 };
});
// 最新状態のstateにアクセス可能なので、この計算は「1 + 1」となる
this.setState(function (state, props) {
return { count: state.count + 1 };
});
},
render: function () {
return <div onClick={this.handleClick}>Count: {this.state.count}</div>;
},
});
React.render(<Hello />, document.body);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment