The source code for this blog is available on GitHub.

Blog.

Reactを学ぶ8

Cover Image for Reactを学ぶ8

公式の1~9までをなぞった。 https://ja.reactjs.org/docs/hello-world.html

作業時間:4時間 経過時間:28.5時間

以下のコマンドでお手軽にプロジェクトを作れた。

create-react-app

create-react-appとは。 npm run testすると、Jestが走った。 コマンドすると、オプションが複数あった。

なんか良い感じに隠蔽してくれているらしい。 npm run ejectすると、隠蔽されていた設定ファイル等がむき出しになる。 カスタマイズするには、これを実行してからできる。

bashとzshが生きているのが酷いが、とりあえずnpmを生き返すために、調整だけした。

open ~/.bash_profile
source ~/.bash_profile

open ~/.zprofile
source ~/.zprofile

create-react-app

メモ

ReactDOM.render(
  <React.StrictMode> //"use strict";
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

imageをインポートすると、相対パスが返される。

import from '{image path}'

cssをインポートすると、どうなっているんだろうか。 中を見たが、空のオブジェクトが返される。 だけど、cssは適用されている。 どこで何が解決されているのだろう?

import a from './App.css';

function App(){
 console.log(a)
  return (
  ...
  )
}

props

コンポーネントの名前は大文字から始めないと有効じゃない。

JSX とは、つまるところ React.createElement(component, props, ...children) の糖衣構文にすぎません。

propsはオブジェクト。コンポーネント定義で関数を定義しているが、やりとりするデータはオブジェクトでやりとしている。 糖衣構文を使用している場合は、背景をしった方がいい。でないと、データの流れが追いにくかった。

propsは読み取り専用であること。

stateとライフサイクル

class Clock extends React.Component{
  constructor(props){
    super(props);
    this.state = {date: new Date()};
  }
  componentDidMount(){
    this.timerID = setInterval(
      () => this.tick(),
      1000
    )
  }
  componentWillUnmount(){
    clearInterval(this.timerID);
  }

  tick(){
    this.setState({
      date: new Date()
    })
  }

  render(){
    return (
      <div>
        <h1>hello,world</h1>
        <h2>it is {this.state.date.toLocaleTimeString()}</h2>
      </div>
    )
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
)

Event

class Toggle extends React.Component {
  constructor(props){
    super(props);
    this.state = {isToggleOn: true};
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState(state =>({
      isToggleOn: !state.isToggleOn
    }))
  }
  render(){
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON': "OFF"}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
)

条件付きレンダー

const UserGreeting = props =>{
  return <h1>welcome back!</h1>
}
const GuestGreeting = props => {
  return <h1>please sign up</h1>
}

const Greeting = props =>{
  const isLoggedIn = props.isLoggedIn;
  if(isLoggedIn){
    return <UserGreeting />
  }
  return <GuestGreeting />
}

const LoginButton = props =>{
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  )
}
const LogoutButton = props =>{
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  )
}

class LoginControl extends React.Component {
  constructor(props){
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false}
  }
  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }
  handleLogoutClick(){
    this.setState({isLoggedIn: false})
  }
  render(){
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if(isLoggedIn){
      button = <LogoutButton onClick={this.handleLogoutClick} />
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />
    }
    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    )
  }
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('root')
)

true && expressionはtrue,false && expressionはfalse

function MailBox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
      <h2>
        You hav {unreadMessages.length} unread messages;
      </h2>
      }
    </div>
  )
}

const messages = ['react','re:react','re:re: react'];
ReactDOM.render(
  <MailBox unreadMessages={messages} />,
  document.getElementById('root')
)

コンポーネントでnullを返せる

const WarningBanner = props =>{
  if(!props.warn)return null;
  return (
    <div className="warning">
      warning
    </div>
  )
}

class Page extends React.Component{
  constructor(props){
    super(props);
    this.state = {showWarning: true};
    this.handleToggleClick = this.handleToggleClick.bind(this)
  }
  handleToggleClick() {
    this.setState(state => ({
      showWarning: !state.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')
)

リストと key

listを作成する場合、key属性がいる。 keyがいる理由は、差分検出処理で機能するため。

const NumberList = props =>{
  const numbers = props.numbers;
  const listItems = numbers.map((number)=>
    <li key={number.toString()}>{number}</li>
  )
  return (
    <ul>{listItems}</ul>
  )
}

const numbers = [1,2,3,4,5];

ReactDOM.render(
  <NumberList numbers={numbers}/>,
  document.getElementById('root')
)

mapでインライン化できる

const ListItem = props =>{
  return <li>{props.value}</li>
}

const NumberList = props =>{
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number)=>
      <ListItem key={number.toString()} value={number} />
      )}
    </ul>
  )
}

const numbers = [1,2,3,4]

ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
)

form

制御されたコンポーネント、stateでデータを管理する textareaはvalueで管理される

class NameForm extends React.Component {
  constructor(props){
    super(props);
    this.state = {value: 'coconut'};
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({value: event.target.value})
  }
  handleSubmit(event) {
    alert('A name was submitted:' + this.state.value)
    event.preventDefault();
  }
  render(){
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          pick your favorite:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
          {/* <input type="text" value={this.state.value} onChange={this.handleChange} /> */}
          {/* <textarea value={this.state.value} onChange={this.handleChange} /> */}
        </label>
        <input type="submit" value="Submit" />
      </form>
    )
  }
}

computed property nameを知る。 オブジェクトのキーに変数を割り当てて使用するには、ブラケットを用いれば可能だった。

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };
    this.handleInputChange = this.handleInputChange.bind(this)
  }
  handleInputChange(event) {
    const target = event.target;
    const value = target.name = 'isGoing' ? target.checked : target.value;
    const name = target.name;
    this.setState({
      [name]: value
    })
  }
  render() {
    return (
      <form>
        <label>
          is going:
          <input
            name="isGoing"
            type="checkbox"
            checke={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(
  <Reservation />,
  document.getElementById('root')
)

More Stories