React項目詳解

原文連接: React項目詳解
GITHUB: react-webpack-config
持續更新...

React項目指導

使用webpack須要安裝的依賴

  1. webpackwebpack-clireactreact-dom
  2. babel-loader@babel/core@babel/preset-env@babel/preset-react
  3. 設置.babelrc{"presets": ["@babel/preset-env","@babel/preset-react"]}
  4. 設置scriptsjavascript

    "dev": "webpack --mode development",
     "build": "webpack --mode production"
  5. 設置webpack-dev-serverjava

    devServer: {
      compress: true,
        port: 9000,
        hot: true
    },
    
    "start": "webpack-dev-server --config webpack.config.js"
  6. 設置performancereact

    performance: {
      hints: false
    }

Component

  1. 基本組件webpack

    let title = <h1>Hello, world!</h1>
    
    ReactDOM.render(title,document.getElementById('root'))
  2. 動態組件git

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    let displayTime = () => {
      let nowTime = (
        <div>
          <span>如今時間:{new Date().toLocaleTimeString()}</span>
        </div>
      );
      ReactDOM.render(t
        nowTime,
        document.getElementById('root')
      );
    };
    
    setInterval(displayTime, 1000);
  3. class組件構建器github

    import React, {Component} from 'react';
    import ReactDOM from 'react-dom';
    
    class HelloTitle extends Component {
      render() {
        return <h1>Hello,World!</h1>
      }
    }
    
    ReactDOM.render(
      <HelloTitle/>,
      document.getElementById('root')
    );
  4. props屬性web

    import React, {Component} from 'react';
    import ReactDOM from 'react-dom';
    
    class HelloTitle extends Component {
      render() {
        return <h1>Hello,{this.props.name}!</h1>
      }
    }
    
    let titleDiv = (
      <div>
        <HelloTitle name="React"/>
        <HelloTitle name="World"/>
      </div>
    );
    
    ReactDOM.render(
      titleDiv,
      document.getElementById('root')
    );
  5. props多層使用服務器

    import React, {Component} from 'react';
    import ReactDOM from 'react-dom';
    
    class HelloTitle extends Component {
      render() {
        return <h1>Hello,{this.props.name}!</h1>
      }
    }
    
    class HelloDiv extends Component {
      render() {
        return <div><HelloTitle name={this.props.name}/></div>
      }
    }
    
    ReactDOM.render(
      <HelloDiv name="React"/>,
      document.getElementById('root')
    );
  6. 組件複用babel

    import React, {Component} from 'react';
    import ReactDOM from 'react-dom';
    
    class HelloTitle extends Component {
      render() {
        return <h1 style={this.props.style}>{this.props.content}</h1>
      }
    }
    
    class HelloDiv extends Component {
      render() {
        return <div>
          <HelloTitle content="比較大的字" style={{'fontSize': 18}}/>
          <HelloTitle content="比較小的字" style={{'fontSize': 12}}/>
        </div>
      }
    }
    
    ReactDOM.render(
      <HelloDiv/>,
      document.getElementById('root')
    );

Component的狀態state和生命週期

state屬性

constructor(props) {
  super(props);
  this.state = {
    time: new Date().toLocaleTimeString()
  }
}

render() {
  return <h1>如今時間是{this.state.time}</h1>
}

組件構建完成後先執行的動做,componentDidMount()網絡

componentDidMount() {
  let upTime = () => {
    this.setState({time: new Date().toLocaleTimeString()})
  };
  setInterval(upTime, 1000)
}

setState()修改狀態值

this.setState({time: new Date().toLocaleTimeString()})

生命週期

  1. constructor中初始化組件內部的資料。
  2. 使用render()在網頁上輸出組件內容。
  3. 輸出後會執行componentDidMount()進行一次調用。
  4. 當組件內部的state值被修改時執行componentDidUpdate()
  5. 當組件被移除時會執行componentWillUnmount()的內容一次。

componentDidMount()

  1. Component已經render到實體DOM階段完成的時候觸發;
  2. method只會被呼叫一次;
  3. 在這裡能夠setState(),並會再次從新rendercomponent一次;
  4. 能夠放入具備side effectfunction,如setInterval、呼叫API等等。

componentWillUnmount()

  1. Component即將從實體DOM階段移除「以前」的時候觸發;
  2. 也是隻會被呼叫一次;
  3. 不能夠在這裡使用setState()
  4. 也能夠放入具備side effectfunction
class Clock extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentTime: new Date().toLocaleString()
    }
  }

  componentDidMount() {
    this.timer = setInterval(this.updateTime, 1000)
  }

  componentWillUnmount() {
    clearInterval(this.timer)
  }

  updateTime = () => {
    this.setState({
      currentTime: new Date().toLocaleString()
    })
  };

  render() {
    const {currentTime} = this.state;
    return (
      <div className="clock">
        <div>{currentTime}</div>
      </div>
    )
  }
}

component各階段的生命週期方法

  1. 掛載(Mounting):組件一開始呈現到真實網頁的過程
class LifeCycle extends Component {
  constructor(props) {
    super(props);
    //  建構式,推動組件初始state設定,綁定方法,接受父級props的地方
    //  只會被調用一次
    //  不適合使用具備side effect的工做,如AJAX調用
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    //  React V16.3新增的生命週期方法
    //  在組件建構以後,render()被調用以前
    //  每次接收新的props後回傳object更新state;也能夠回傳null不作更新
  }

  UNSAFE_componentWillMount() {
    //  即將在React V17中移除,不可與static getDerivedStateFromProps同時出現
    //  在render()調用以後被調用,且以後被調用一次
    //  會用到的地方僅在於服務器端的應用
    //  適合設定初始化數據的地方,不適合作有side effect的工做
  }

  render() {
    //  在class component中「惟一」必要存在的方法
    //  儘可能pure,不該該改變component的state
    //  如須要和browser有互動的話,將其放進componentDidMount()中
    return (
      <div></div>
    )
  }

  componentDidMount() {
    //  在render()被調用後,組件已經在真實DOM呈現以後被調用執行
    //  只會被調用一次,適合執行AJAX、計時器設定、加入eventListener等有反作用的工做
    //  調用setState()可能會再執行一次render()
  }
}
  1. 更新(Updating):使用者的操做中,組件的狀態和屬性被改變
class LifeCycle extends Component {
  UNSAFE_componentWillReceiveProps(nextProps) {
    //  即將在React V17中移除,不可與static getDerivedStateFromProps同時出現
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    //  React V16.3新增的新生命週期方法
    //  在組件構建以後,render()被調用以前
    //  每次接收新的props後回傳object更新state;也可回傳null不作更新
    //  搭配componentDidUpdate,能夠達到與componentWillReceivedProps的效果
  }

  shouldComponentUpdate() {
    //  讓使用者自定義是否進行component更新,預設都會默認更新
    //  只有少部分的狀況纔會使用此方法,如進行復雜耗時的計算
    //  儘可能不要檢查太過深層的值,也不適合執行具備side effect的工做
  }

  UNSAFE_componentWillUpdate(nextProps, nextState) {
    //  即將在React V17中移除,不可與static getDerivedStateFromProps同時出現
    //  在組件即將更新「以前」調用
    //  不能在此使用setState方法,若要改變請用getDerivedStateFromProps
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    //  在render以後,在最新的渲染前輸出交給真實DOM前會當即執行
    //  返回的值做爲componentDidUpdate的第三個值
    //  搭配componentDidUpdate,能夠達到與componentWillUpdate的效果
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    //  會發生在更新「以後」被調用
    //  若是shouldComponentUpdate回傳的是false,就不會調用此方法
    //  適合執行具備side effect的地方
  }
}
  1. 卸載(Unmounting):組件要移除真實DOM的階段
class LifeCycle extends Component {
  componentWillMount() {
    //  在組件即將要移除真實DOM時會執行
    //  能夠在這裏作中斷網絡鏈接、清楚計時器、移除事件監聽器
    //  不適合使用setState方法
  }
}

catch error

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    }
  }

  componentDidCatch(error, info) {
    //  是error handle的方法,與掛載和更新階段有密切關係
    //  能夠用來捕捉子組件的任何JavaScript的錯誤
    //  能夠記錄這些錯誤,或是呈如今目前反饋用的操做界面上
    //  不能在事件的callback上使用

    //  顯示在反饋的UI上
    this.setState({hasError: true});
    //  也能夠用額外的錯誤記錄服務
    logErrorMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong...</h1>
    }
    return this.props.children
  }
}

上面就是component可使用的生命週期方法,最經常使用主要是這些:

  1. constructor()
  2. render()
  3. componentDidMount()
  4. compoinentDidUpdate()
  5. componentWillUnmount()

Component的事件處理

  1. 取得觸發事件的DOM
class InputGender extends Component {
  constructor(props) {
    super(props);
    this.state = {
      gender: ''
    };
    this.changeGender = this.changeGender.bind(this)
  }

  changeGender(event) {
    console.log(event.target.value);
    this.setState({
      gender: event.target.value
    });
  }

  componentDidUpdate() {
    console.log(`已將state.gender變更爲:${this.state.gender}`)
  }

  render() {
    return (
      <select onChange={this.changeGender}>
        <option value="M">男</option>
        <option value="W">女</option>
      </select>
    )
  }
}
class HelloTitle extends Component {
  render() {
    return <h1>{this.props.title}</h1>
  }
}

{(this.state.gender === 'M') ? <HelloTitle title="先生"/> : <HelloTitle title="女士"/>}

用綁定的state取得輸入資料

class EasyForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ""
    };
    this.changeState = this.changeState.bind(this);
    this.submitForm = this.submitForm.bind(this);
  }

  changeState(event) {
    this.setState({
      name: event.target.value,
    });
  }

  submitForm(event) {
    let element = document.querySelector('span');
    element.innerHTML = `${this.state.name}`;
    event.preventDefault()
  }

  render() {
    return (
      <div>
        <form onSubmit={this.submitForm}>
          <label>姓名:</label>
          <input id="name" name="name" onChange={this.changeState} value={this.state.name}/>
          <input type="submit" value="提交" style={{'marginLeft': 6}}/>
        </form>
        <p>如今輸入的名字是:<span style={{'color': '#FF7F24'}}></span></p>
      </div>
    )
  }
}

受控組件

class EasyForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      lists: [
        {id: '01', listName: '寫文章', check: false},
        {id: '02', listName: '寫代碼', check: false},
        {id: '03', listName: '旅遊', check: true},
        {id: '04', listName: '踢球', check: true},
        {id: '05', listName: '公益', check: false},
      ]
    };
    this.submitForm = this.submitForm.bind(this);
    this.changeState = this.changeState.bind(this);
  }

  changeState(index) {
    let arrLists = this.state.lists;
    arrLists[index].check ? arrLists[index].check = false : arrLists[index].check = true;
    this.setState({
      lists: arrLists,
    });
  }

  submitForm(event) {
    let status = "目前作了:";
    this.state.lists.map((list) => list.check ? status += `${list.listName} ` : '');
    console.log(status);
    event.preventDefault()
  }

  render() {
    let lists = this.state.lists.map((list, index) => (
      <div key={list.id}>
        <input type="checkbox"
               checked={list.check}
               onChange={this.changeState.bind(this, index)}
               key={list.id}
        />
        <label>{list.listName}</label>
      </div>
    ));
    return (
      <form onSubmit={this.submitForm}>
        <div>
          <label>每日待辦清單:</label>
          {lists}
        </div>
        <input type="submit" value="發送表單"/>
      </form>
    )
  }
}

非受控組件

class EasyForm extends Component {
  constructor(props) {
    super(props);
    this.submitForm = this.submitForm.bind(this);
    this.filebox = React.createRef()
  }

  submitForm(event) {
    console.log(`選擇文檔爲:${this.filebox.current.files[0].name}`)
    event.preventDefault()
  }

  render() {
    return (
      <form onSubmit={this.submitForm}>
        <div>
          <label>上傳文檔:</label>
          <input type="file" ref={this.filebox}/>
        </div>
        <input type="submit" value="送出表單"/>
      </form>
    )
  }
}

refs操做DOM

class App extends Component {
  constructor() {
    super();
    this.state = {
      itemList: []
    };
    this.addFile = React.createRef();
  }

  addItem = () => {
    const {itemList} = this.state;
    const tempList = Object.assign([], itemList);
    if (this.addFile.current.value !== '') {
      tempList.push(this.addFile.current.value);
    }
    this.setState({
      itemList: tempList
    });
    this.addFile.current.value = ''
  };

  render() {
    const {itemList} = this.state;
    return (
      <div className="App">
        <input type="text" name="addFile" ref={this.addFile}/>
        <input type="button" onClick={this.addItem} value="ADD"/>
        <ul className="list">
          {itemList.map((item, index) =>
            <li key={`item_${index}`}>{item}</li>
          )}
        </ul>
      </div>
    );
  }
}

實例:TODOLIST

TodoList.js

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: []
    }
  }

  addItem = (text) => {
    const {list} = this.state;
    if (text !== '') {
      const tempArr = list.concat({
        id: list.length + 1,
        text,
        status: false
      });
      this.setState({list: tempArr})
    }
  };

  toggleStatus = (id) => {
    const {list} = this.state;
    const tempArr = list.map(item => {
      if (item.id.toString() === id.toString()) {
        return ({
          id: item.id,
          text: item.text,
          status: !item.status
        })
      }
      return item;
    });
    this.setState({list: tempArr})
  };

  render() {

    const {list} = this.state;
    const divStyle = {
      width: '250px',
      margin: 'auto',
      textAlign: 'left'
    };
    return (
      <div style={divStyle}>
        <TodoForm onAddItem={this.addItem}/>
        <ul>
          {list.map(item => (
            <TodoItem
              key={item.id}
              id={item.id}
              status={item.status}
              onItemClick={this.toggleStatus}
            >
              {item.text}
            </TodoItem>
          ))}
        </ul>
      </div>
    )
  }
}

TodoForm.js

class TodoForm extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  formSubmit = (e) => {
    const {onAddItem} = this.props;
    e.preventDefault();
    onAddItem(this.inputRef.current.value);
    this.inputRef.current.value = ''
  };

  render() {
    return (
      <form onSubmit={this.formSubmit}>
        <input type="text" name="todoItem" ref={this.inputRef} autoComplete="off"/>
        <button type="submit" value="submit">submit</button>
      </form>
    )
  }
}

TodoItem.js

class TodoItem extends Component {
  handleItemClick = (e) => {
    const {onItemClick} = this.props;
    onItemClick(e.target.id)
  };

  render() {
    const {children, id, status} = this.props;
    return (
      <li
        id={id}
        onClick={this.handleItemClick}
        data-status={status}
        style={
          status ?
            {textDecoration: 'line-through'} :
            {textDecoration: 'none'}
        }
      >
        {children}
      </li>
    )
  }
}
相關文章
相關標籤/搜索