React筆記(done)

React筆記

本人在菜鳥教程網站學習React的記錄、記錄、記錄,看到文章的你可以忽略或者轉到
https://www.runoob.com/react/react-tutorial.html

<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

一、JSX

  1. 在jsx中使用表達式 {1+1}

    <h1>{1+1}</h1>
    
  2. 在jsx中不能使用if else語句,但可以使用三元表達式

    <h1>{i == 1 ? 'True!' : 'False'}</h1>
    
  3. 樣式:React推薦使用內聯樣式。可以使用camelCase(駝峯命名法),React會在指定元素數字後自動添加px。

    var myStyle = {
        fontSize: 100,
        color: '#FF0000'
    };
    ReactDOM.render(
        <h1 style = {myStyle}>內聯樣式</h1>,
        document.getElementById('example')
    );	
    
  4. 註釋: 註釋需要寫在花括號中

  5. 數組:jsx允許在模板中插入數組,數組會自動展開所有成員

二、組件

注意:
  • 原生HTML元素名以小寫字母開頭,而自定義的React類名以大寫字母開頭。
  • 組件類只能包含一個頂級標籤,否則也會報錯。
  • 在添加屬性時,class屬性需要寫成className,for屬性寫成htmlFor,因爲class和for是JavaScript的保留字。
/*使用函數定義組件*/
  function HelloMessage(props) {
    return <h1>Hello World!</h1>;
  }
  const element = <HelloMessage/>;

  ReactDOM.render(
    element,
    document.getElementById('example')
  )
  /* 使用 ES6 class 來定義組件 */
  class Welcome extends React.Component {
    // 添加生命週期函數 componentDidMount 在第一次渲染後調用,只在客戶端。
    componentDidMount() {}
    render() {
      return <h1>Hello, world!</h1>
    }
  }
  const element6 = <Welcome/>
  ReactDOM.render(
    element6,
    document.getElementById('example1')
  )
區別 函數組件 類組件
是否有 this 沒有
是否有生命週期 沒有
是否有狀態 state 沒有
怎樣選擇組件
  • 如果組件需要有自己的私有數據。推薦使用class創建有狀態組件;
  • 如果組件沒有自己的私有數據。推薦使用函數創建無狀態組件;
  • 官方:由於無狀態組件沒有自己的state和生命週期函數,所以運行效率會比有狀態組件高一些。
組件中props和state/data之間的區別
  • props中的數據,都是外界傳遞過來的;
  • props中的數據,不能重新賦值;
  • state/data中的數據,都是組件私有的;
  • state/data中的數據,可以賦值。 setState ()

三、State狀態

React把組件看成是一個狀態機(State Machines)。通過與用戶的交互,實現不同狀態,然後渲染UI,讓用戶界面和數據保持一致。

React裏只需更新組件的state,然後根據新的state重新渲染用戶界面(不需要操作DOM)

function FormattedDate(props) {
    return <h2>現在是{props.date.toLocaleTimeString()}.</h2>

}

class Clock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
    }

    /* componentDidMount 在第一次渲染後調用 */
    componentDidMount() {
        this.timeID = setInterval(
            () => this.tick(),
            1000
        );
    }

    /* componentWillUnmount在組件從 DOM 中移除之前立刻被調用。 */
    componentWillUnmount() {
        clearInterval(this.timeID);
    }

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

    render() {
        return (
            <div>
                <FormattedDate date={this.state.date}/>
            </div>
        );
    }
}

/*
  爲了表明所有組件都是真正隔離的,我們可以創建一個 App 組件,它渲染三個Clock:
   實例中每個 Clock 組件都建立了自己的定時器並且獨立更新。
   */
function App() {
    return (
        <div>
            <Clock/>
            <Clock/>
            <Clock/>
        </div>
    )
}

ReactDOM.render(
    <App/>,
    document.getElementById('example')
)

四、React Props

state和props主要區別在於props是不可改變的,而state是可以根據與用戶交換來改變。

這就是爲什麼有些容器組件需要定義state來更新和修改數據。而子組件只能通過props來傳遞數據。

默認Props

可以通過 組件.defaultProps 設置默認Props

HelloMessage.defaultProps = {
	name: "abc"
}
Props驗證
<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>

Props驗證使用propTypes,它可以保證我們的應用組件被正確使用,React.PropTypes提供很多驗證器(validator)來驗證傳入數據是否有效。當像props傳入無效數據時,JavaScript控制檯會拋出警告。

var title = "菜鳥教程";
// var title = 123;
class MyTitle extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.title}</h1>
    );
  }
}
 
MyTitle.propTypes = {
  title: PropTypes.string
};
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example')
);

五、React事件處理

React元素的事件處理和DOM元素類似,但是有語法上的不同:

  • React事件綁定屬性的命名採用駝峯式寫法,而不是小寫;
  • 如果採用JSX的語法需要傳入一個函數作爲事件處理函數,而不是一個字符串(DOM元素的寫法)
<!--html寫法-->
<button onclick="activateLasers()">激活按鈕</button>

<!--React寫法-->
<button onClick={activateLasers}>激活按鈕</button>
  • React中不能使用返回false的方式阻止默認行爲,必須使用preventDefault
function ActionLink() {
    function handleClick(e) {
        e.preventDefault();
        console.log('鏈接被點擊!')
    }
    return (
        <a href="#" onClick={handleClick}>點擊我</a>
    )
}
ReactDOM.render(
    <ActionLink/>,
    document.getElementById('example')
)

實例中,e 是一個合成事件。

使用React的時候通常你不需要使用addEventListener爲已創建的DOM元素添加監聽器。你僅僅需要在這個元素初始渲染的時候提供一個監聽器。

當你使用ES6 class語法來定義一個組件的時候,事件處理器會成爲類的一個方法。

JSX回調函數中的this綁定

類的方法默認不會綁定this,如果不綁定,調用時this的值會是undefined

  1. 可使用bind在構造函數中綁定this

    class LoggingButton extends React.Component {
      constructor() {
        super();
        this.handleClick = this.handleClick.bind(this)
      }
      //  這個語法確保了 this 綁定在 handleClick中
      handleClick () {
        console.log(`this is : ${this}`)
      }
      render() {
        return (
          <button onClick={this.handleClick}>click me</button>
        )
      }
    }
    
  2. 使用屬性初始化器語法綁定回調函數

    class LoggingButton extends React.Component {
        //  這個語法確保了 this 綁定在 handleClick中
        handleClick = () => {
            console.log(`this is : ${this}`)
        }
        render() {
            return (
                <button onClick={this.handleClick}>click me</button>
            )
        }
    }
    ReactDOM.render(
        <LoggingButton/>,
        document.getElementById('example')
    )
    
  3. 如果沒有使用屬性初始化器語法,可以在回調函數中使用箭頭函數(不推薦)

    class LoggingButton extends React.Component {
      handleClick() {
        console.log('this is:', this);
      }
      render() {
        //  這個語法確保了 `this` 綁定在  handleClick 中
        return (
          <button onClick={(e) => this.handleClick(e)}>
            Click me
          </button>
        );
      }
    }
    

    問題:使用這個語法有個問題就是每次LoggingButton渲染的時候都會創建一個不同的回調函數。在大多數情況下沒有問題。如果這個回調函數作爲一個屬性值傳入低階組件,這些組件可能會進行額外的重新渲染。通常建議在構造函數中綁定或使用屬性初始化器語法來避免這類性能問題。

向事件處理程序傳遞參數

方法:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

兩個方法是等價的。

上面例子中:參數e作爲React的事件對象將會被作爲第二個參數進行傳遞。通過箭頭函數的方式,事件對象必須顯式的進行傳遞,但是通過bind的方式,事件對象以及更多的參數將會被隱式的進行傳遞。

注意:通過bind方式向監聽函數傳參,在類組件中定義的監聽函數,事件對象e要排在所傳遞的參數的後面,如:

class Popper extends React.Component {
    constructor() {
      super();
      this.state = {name: 'Hello world!'}
    }
    preventPop(name, e){ // 事件對象e要放在最後
      e.preventDefault();
      console.log(this.state.name);
    }
    render(){
      return (
        <div>
          <p>Hello</p>
          {/*通過bind()方法傳遞參數。*/}
          <a href="http://aixiaodou.cn" onClick={this.preventPop.bind(this, this.state.name)}>Click</a>
        </div>
      )
    }
  }
  ReactDOM.render(
    <Popper/>,
    document.getElementById('example')
  )

六、條件渲染

渲染
  • React中可以封裝不同的組件,然後根據需要來渲染要顯示的組件。

  • React中的條件渲染和javascript中的一致,使用if或條件運算符來創建表示當前狀態的元素。

元素變量

可以使用變量來存儲元素。它可以幫助你有條件的渲染組件的一部分,而輸出其他部分不會更改。

下面創建一個名爲LoginControl的有狀態的組件。

他會根據當前的狀態來渲染<LoginBUtton/><LogoutButton/>,它也將渲染前面例子中的<Greeting/>

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>
    );
  }
}

function UserGreeting(props) {
  return <h1>歡迎回來!</h1>;
}

function GuestGreeting(props) {
  return <h1>請先註冊。</h1>;
}

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

function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      登陸
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      退出
    </button>
  );
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('example')
);
與運算符 &&
isTrue && <h1>與運算符</h1>

可以通過花括號包裹代碼在JSX中嵌套任何表達式,也包括JavaScript的邏輯與&&,它可以方便地條件渲染一個元素。

在JavaScript中,true && expression總是返回 expression,而false && expression 總是返回false,因此如果條件是true,&&右側的元素就會被渲染,如果是false,React會忽略並跳過它。

三目運算符
condition ? true: false
阻止組件渲染

在極少數情況下,可能希望隱藏組件,及時它被其他組件渲染。讓render方法返回null,而不是他的渲染結果即可實現。

組件的render方法返回null並不會影響該組件生命週期方法的回調。

七、列表&keys

列表

可以使用JavaScript中的map()方法來創建列表。

每個列表元素分配一個key,不然會出現警告a key should be provided for list items

function 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];
  ReactDOM.render(
    <NumberList numbers={numbers}/>,
    document.getElementById('example')
  );
keys
  • keys可以在DOM中的某些元素被增加或刪除的時候幫助React識別哪些元素髮生了變化。因此應當給數組中的每一個元素賦予一個確定的標識。

  • 一個元素的key最好是這個元素在列表中擁有的一個獨一無二的字符串。通常,我們使用來自數據的id作爲元素的key。

  • 當元素沒有確定的id時,可以使用它的序列號索引index作爲key。

  • 如果列表可以重新排序,我們不建議使用索引來進行排序,因爲這會導致渲染變得很慢。

  • 元素的key只有在它和它的兄弟節點對比時纔有意義。

function ListItem(props) {
  // 這裏不需要指定key:
  return <li>{props.value}</li>;
}

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

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('example')
);

八、組件API

  • 設置狀態:setState
  • 替換狀態:replaceState
  • 設置屬性:setProps
  • 替換屬性:replaceProps
  • 強制更新:forceUpdate
  • 獲取DOM節點:findDOMNode
  • 判斷組件掛載狀態:isMounted
設置狀態:setState
setState(object nextState[,function callback])

參數說明:

  • nextState:將要設置的新狀態,該狀態會和當前的state合併
  • callback:可選參數,回調函數。該函數會在setState設置成功,且組件重新渲染後調用。

合併nextState和當前state,並重新渲染組件。setState是React事件處理函數中和請求回調函數中觸發UI更新的主要方法。

關於setState:

  • 不能在組件內部通過this.state修改狀態,因爲該狀態會在調用setState()後被替換;
  • setState()並不會立即改變this.setState,而是創建一個即將處理的state。setState()並不是同步的,爲了提升性能React會批量執行state的DOM渲染;
  • setState()總是會觸發一次組件重繪,除非在shouldComponentUpdate()中實現了一些條件渲染邏輯。
class Counter extends React.Component{
  constructor(props) {
    super(props);
    this.state = {clickCount: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => {
      return {clickCount: state.clickCount + 1}
    })
  }
  render() {
    return (
      <h2 onClick={this.handleClick}>點我!點擊次數:{this.state.clickCount}}</h2>
    )
  }
}
ReactDOM.render(
  <Counter/>,
  document.getElementById('example')
);
替換狀態: replaceState
replaceState(object nextState[, function callback])
  • nextState:將要設置的新狀態,該狀態會替換當前的state
  • callback:可選參數,回調函數。該函數會在setState設置成功,且組件重新渲染後調用。

replaceState()方法與setState()類似,但是方法只會保留nextState中狀態,原state不在nextState中的狀態都會被刪除。

設置屬性:setProps
setProps(object nextProps[, function callback])
替換屬性:replaceProps
replaceProps(object nextProps[, function callback])
強制更新: forceUpdate
forceUpdate([function callback])
獲取DOM節點:findDOMNode
DOMElement findDOMNode()
判斷組件掛載狀態:isMounted
bool isMounted()

返回值:true或false

九、組件生命週期

三個狀態
  • Mounting:已插入真實DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真實DOM
生命週期方法
  • componentWillMount:在渲染前調用,在客戶端也在服務端。

  • componentDidMount:在第一次渲染後調用,只在客戶端。

  • componentWillReceiveProps:在組件接收到一個新的prop(更新後)時被調用。在初始化render時不會被調用。

  • shouldComponentUpdate:返回一個布爾值。在組件接收到新的props或者state時被調用。在初始化時或者使用forceUpdate時不被調用。

    可在確認不需要更新組件時使用。

  • componentWillUpdate:在組件接收到新的props或者state但還沒有render時被調用。初始化時不會被調用。

  • componentDidUpdate:在組件完成更新後立即調用。初始化時不會被調用。

  • componentWillUnmount:在組件從DOM中移除之前立即被調用。

十、表單與事件

HTML表單元素與React中的其它DOM元素有所不同,因爲表單元素生來就保留一些內部狀態。

在 HTML 當中,像 <input>,<textarea>, 和<select> 這類表單元素會維持自身狀態,並根據用戶輸入進行更新。但在React中,可變的狀態通常保存在組件的狀態屬性中,並且只能用 setState() 方法進行更新。

input輸入框

使用onChange事件響應更新用戶輸入的值。

在子組件上使用表單時,onChange方法將處罰state的更新並將更新的值傳遞到子組件的輸入框的value上來重新渲染頁面。

需要在父組件通過創建事件句柄 (handleChange) ,並作爲 prop (updateStateProp) 傳遞到你的子組件上。

class Content extends React.Component{
    render() {
      return  <div>
          <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp}/>
          <h4>{this.props.myDataProp}</h4>
        </div>;
    }
  }

  class HelloMessage extends React.Component {
    constructor(props) {
      super(props);
      this.state = {value: 'Hello.'};
      this.handleChange = this.handleChange.bind(this);
    }
    handleChange (event) {
      this.setState({value: event.target.value});
    }
    render() {
      var value = this.state.value;
      return <div>
        <Content myDataProp = {value} updateStateProp = {this.handleChange}/>
      </div>;
    }
  }
  ReactDOM.render(
    <HelloMessage/>,
    document.getElementById('example')
  );
select下拉菜單

在React中,不適用selected屬性,而在根select標籤上用value屬性來表示選中項。

多個表單

當有多個input時,可以通過給每個元素添加一個name屬性,來處理函數根據event.target.name的值來選擇做什麼。

class Reservation extends React.Component{
    constructor(props) {
      super(props);
      this.state = {
        isGoing: true,
        number1: 1,
        number2: 3
      };

      this.handleInputChange = this.handleInputChange.bind(this);
    }
    handleInputChange(event) {
      const target = event.target;
      //根據type來判斷取checked值還是取value值
      const value = target.type === 'checkbox' ? target.checked: target.value;
      const name = target.name
      this.setState({
        [name]: value
      });
    }

    render() {
      return (
        <form>
          <label>
            是否離開:
            <input
              type="checkbox"
              name="isGoing"
              checked={this.state.isGoing}
              onChange={this.handleInputChange}/>
          </label>
          <label>
            訪客數:
            <input
              type="number"
              name="number1"
              value={this.state.number1}
              onChange={this.handleInputChange}/>
          </label>
          <label>
            訪客數:
            <input
              type="number"
              name="number2"
              value={this.state.number2}
              onChange={this.handleInputChange}/>
          </label>

        </form>
      )
    }
  }

  ReactDOM.render(
    <Reservation/>,
    document.getElementById('example')
  );

當需要從子組件中更新state時,需要在父組件通過創建事件句柄(handleChange),並作爲prop(updateStateProp)傳遞到你的字組件上。

function Content(props) {
    return (
      <div>
        <button onClick={props.updateStateProp}>點我</button>
        <h4>{props.myDataProp}</h4>
      </div>
    )
  }
  class HelloMsg extends React.Component{
    constructor(props) {
      super(props);
      this.state = {
        value: 'hello A'
      };
      this.handleChange = this.handleChange.bind(this);
    }
    handleChange(event) {
      this.setState({value: 'webcome A'})
    }
    render() {
      var value = this.state.value;
      return <div>
        <Content myDataProp={value} updateStateProp={this.handleChange}/>
      </div>
    }
  }

  ReactDOM.render(
    <HelloMsg/>,
    document.getElementById('example')
  );

十一、React Refs

React支持一種非常特殊的屬性 Ref,你可以用來綁定到render()輸出任何組件上。

這個特殊的屬性允許你引用render()返回的響應的支撐實例(backing instance)。這樣就可以確保在任何時間總是拿到正確的實例。

Refs 使用方法

綁定一個ref屬性到render的返回值上:

<input ref="myInput"/>

在其它代碼中,通過this.refs獲取支撐實例:

var input = this.refs.myInput;
var inputValue = input.value;
var inputRect = input.getBoundingClientRect();
Refs 完整實例
class MyComponent extends React.Component{
    handleClick(event) {
      // 使用原生的DOM API 獲取焦點
      this.refs.myInput.focus();
    }
    render() {
      // 當組件插入到DOM後,ref屬性添加一個組件的引用於到 this.refs
      return (
        <div>
          <input type="text" ref="myInput"/>
          <input
            type="button"
            value="獲取焦點"
            onClick={this.handleClick.bind(this)}
          />
        </div>
      )
    }
  }

  ReactDOM.render(
    <MyComponent/>,
    document.getElementById('example')
  );

實例中,我們獲取了輸入框的支撐實例的引用,子點擊按鈕後輸入框獲取焦點。

我們也可以使用 getDOMNode()方法獲取DOM元素

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章