日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(七)React使用

發布時間:2023/12/31 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (七)React使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

React使用

    • React vs Vue
    • React使用
    • React面試題
    • React基本使用
      • JSX基本使用
      • 條件判斷
      • 渲染列表
      • 事件
      • 表單
      • 組件使用
      • setState
      • 組件生命周期
        • 單組件生命周期
        • 父子組件生命周期,和Vue的一樣
      • 總結
    • React高級特性
      • 函數組件
      • 非受控組件
        • 使用場景
        • 受控組件 vs 非受控組件
      • Portals
        • 使用場景
      • context
      • 異步組件
      • 性能優化
        • shouldComponentUpdate(簡稱SCU)
        • SCU一定要配合不可變值
        • SCU使用總結
        • PureComponent和React.memo
        • 不可變值immutable.js
        • 性能優化-小結
      • 高階組件HOC
        • 關于組件公共邏輯的抽離
      • Renders Props
        • HOC vs Render Props
      • 知識點總結
    • Redux
      • Redux使用
        • 基本概念
        • 單項數據流
        • react-redux
        • 異步action
        • 中間件
      • Redux知識總結
    • React-router
      • 路由模式
      • React-router總結

React vs Vue

React和Vue一樣重要(特別是大廠面試),力求兩者都會
React和Vue有很多相通之處,而且正在趨于一致
React比Vue學習成本高,尤其對于初學者

React使用

基本使用–常用,必須會
高級特性–不常用,但體現深度
Redux和React-router使用

React面試題

React組件如何通訊
JSX本質是什么
context是什么?有何用途
shouldComponentUpdate的用途
描述redux單項數據流
setState是同步還是異步?(場景圖)

React基本使用

JSX基本使用

變量、表達式
class style
子元素和組件

import React from 'react' import './style.css' import List from '../List'class JSXBaseDemo extends React.Component {constructor(props) {super(props)this.state = {name: '雙越',imgUrl: 'https://img1.mukewang.com/5a9fc8070001a82402060220-140-140.jpg',flag: true}}render() {// // 獲取變量 插值// const pElem = <p>{this.state.name}</p>// return pElem// // 表達式// const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>// return exprElem// // 子元素// const imgElem = <div>// <p>我的頭像</p>// <img src="xxxx.png"/>// <img src={this.state.imgUrl}/>// </div>// return imgElem// // class// const classElem = <p className="title">設置 css class</p>// return classElem// // style// const styleData = { fontSize: '30px', color: 'blue' }// const styleElem = <p style={styleData}>設置 style</p>// // 內聯寫法,注意 {{ 和 }}// // const styleElem = <p style={{ fontSize: '30px', color: 'blue' }}>設置 style</p>// return styleElem// 原生 htmlconst rawHtml = '<span>富文本內容<i>斜體</i><b>加粗</b></span>'const rawHtmlData = {__html: rawHtml // 注意,必須是這種格式}const rawHtmlElem = <div><p dangerouslySetInnerHTML={rawHtmlData}></p><p>{rawHtml}</p></div>return rawHtmlElem// // 加載組件// const componentElem = <div>// <p>JSX 中加載一個組件</p>// <hr/>// <List/>// </div>// return componentElem} }export default JSXBaseDemo

條件判斷

if else
三元表達式
邏輯運算符 && ||

import React from 'react' import './style.css'class ConditionDemo extends React.Component {constructor(props) {super(props)this.state = {theme: 'black'}}render() {const blackBtn = <button className="btn-black">black btn</button>const whiteBtn = <button className="btn-white">white btn</button>// // if else// if (this.state.theme === 'black') {// return blackBtn// } else {// return whiteBtn// }// // 三元運算符// return <div>// { this.state.theme === 'black' ? blackBtn : whiteBtn }// </div>// &&return <div>{ this.state.theme === 'black' && blackBtn }</div>} }export default ConditionDemo

渲染列表

map:map返回一個重組數組
key

import React from 'react'class ListDemo extends React.Component {constructor(props) {super(props)this.state = {list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}]}}render() {return <ul>{ /* vue v-for */this.state.list.map((item, index) => {// 這里的 key 和 Vue 的 key 類似,必填,不能是 index 或 randomreturn <li key={item.id}>index {index}; id {item.id}; title {item.title}</li>})}</ul>} }export default ListDemo

事件

bind this
關于event參數
傳遞自定義參數

import React from 'react'class EventDemo extends React.Component {constructor(props) {super(props)this.state = {name: 'zhangsan',list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}]}// 修改方法的 this 指向,在這邊bind初始化時一次就好,性能好,在點擊DOM上bind每次點擊都要bind一次this.clickHandler1 = this.clickHandler1.bind(this)}render() {// // this - 使用 bind// return <p onClick={this.clickHandler1}>// {this.state.name}// </p>// // this - 使用靜態方法// return <p onClick={this.clickHandler2}>// clickHandler2 {this.state.name}// </p>// // event// return <a href="https://imooc.com/" onClick={this.clickHandler3}>// click me// </a>// 傳遞參數 - 用 bind(this, a, b)return <ul>{this.state.list.map((item, index) => {return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>index {index}; title {item.title}</li>})}</ul>}clickHandler1() {// console.log('this....', this) // this 默認是 undefinedthis.setState({name: 'lisi'})}// 靜態方法,this 指向當前實例clickHandler2 = () => {this.setState({name: 'lisi'})}// 獲取 eventclickHandler3 = (event) => {event.preventDefault() // 阻止默認行為event.stopPropagation() // 阻止冒泡console.log('target', event.target) // 指向當前元素,即當前元素觸發console.log('current target', event.currentTarget) // 指向當前元素,假象!!!// 注意,event 其實是 React 封裝的。可以看 __proto__.constructor 是 SyntheticEvent 組合事件console.log('event', event) // 不是原生的 Event ,原生的 MouseEventconsole.log('event.__proto__.constructor', event.__proto__.constructor)// 原生 event 如下。其 __proto__.constructor 是 MouseEventconsole.log('nativeEvent', event.nativeEvent)console.log('nativeEvent target', event.nativeEvent.target) // 指向當前元素,即當前元素觸發console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!// 1. event 是 SyntheticEvent ,模擬出來 DOM 事件所有能力// 2. event.nativeEvent 是原生事件對象// 3. 所有的事件,都被掛載到 document 上// 4. 和 DOM 事件不一樣,和 Vue 事件也不一樣}// 傳遞參數clickHandler4(id, title, event) {console.log(id, title)console.log('event', event) // 最后追加一個參數,即可接收 event} }export default EventDemo

表單

受控組件:input的值受state影響
input textarea select 用valuei
checkbox radio 用 checked

import React from 'react'class FormDemo extends React.Component {constructor(props) {super(props)this.state = {name: '雙越',info: '個人信息',city: 'beijing',flag: true,gender: 'male'}}render() {// // 受控組件(非受控組件,后面再講)// return <div>// <p>{this.state.name}</p>// <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */}// <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>// </div>// textarea - 使用 valuereturn <div><textarea value={this.state.info} onChange={this.onTextareaChange}/><p>{this.state.info}</p></div>// // select - 使用 value// return <div>// <select value={this.state.city} onChange={this.onSelectChange}>// <option value="beijing">北京</option>// <option value="shanghai">上海</option>// <option value="shenzhen">深圳</option>// </select>// <p>{this.state.city}</p>// </div>// // checkbox// return <div>// <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>// <p>{this.state.flag.toString()}</p>// </div>// // radio// return <div>// male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/>// female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/>// <p>{this.state.gender}</p>// </div>// 非受控組件 - 后面再講}onInputChange = (e) => {this.setState({name: e.target.value})}onTextareaChange = (e) => {this.setState({info: e.target.value})}onSelectChange = (e) => {this.setState({city: e.target.value})}onCheckboxChange = () => {this.setState({flag: !this.state.flag})}onRadioChange = (e) => {this.setState({gender: e.target.value})} }export default FormDemo

組件使用

props傳遞數據
props傳遞函數
props類型檢查

/*** @description 演示 props 和事件* @author 雙越老師*/import React from 'react' import PropTypes from 'prop-types'class Input extends React.Component {constructor(props) {super(props)this.state = {title: ''}}render() {return <div><input value={this.state.title} onChange={this.onTitleChange}/><button onClick={this.onSubmit}>提交</button></div>}onTitleChange = (e) => {this.setState({title: e.target.value})}onSubmit = () => {const { submitTitle } = this.propssubmitTitle(this.state.title) // 'abc'this.setState({title: ''})} } // props 類型檢查 Input.propTypes = {submitTitle: PropTypes.func.isRequired }class List extends React.Component {constructor(props) {super(props)}render() {const { list } = this.propsreturn <ul>{list.map((item, index) => {return <li key={item.id}><span>{item.title}</span></li>})}</ul>} } // props 類型檢查 List.propTypes = {list: PropTypes.arrayOf(PropTypes.object).isRequired }class Footer extends React.Component {constructor(props) {super(props)}render() {return <p>{this.props.text}{this.props.length}</p>}componentDidUpdate() {console.log('footer did update')}shouldComponentUpdate(nextProps, nextState) {if (nextProps.text !== this.props.text|| nextProps.length !== this.props.length) {return true // 可以渲染}return false // 不重復渲染}// React 默認:父組件有更新,子組件則無條件也更新!!!// 性能優化對于 React 更加重要!// SCU 一定要每次都用嗎?—— 需要的時候才優化 }class TodoListDemo extends React.Component {constructor(props) {super(props)// 狀態(數據)提升this.state = {list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}],footerInfo: '底部文字'}}render() {return <div><Input submitTitle={this.onSubmitTitle}/><List list={this.state.list}/><Footer text={this.state.footerInfo} length={this.state.list.length}/></div>}onSubmitTitle = (title) => {this.setState({list: this.state.list.concat({id: `id-${Date.now()}`,title})})} }export default TodoListDemo

setState

不可變值(setstate不能提前對state值進行修改,應該什么時候改就什么時候設值,而且設置的時候不能影響之前state的值)
可能是異步更新
可能會被合并

import React from 'react'class ListDemo extends React.Component {constructor(props) {super(props)this.state = {count: 0}}render() {return <p>{this.state.count}</p>}componentDidMount() {// count 初始值為 0this.setState({ count: this.state.count + 1 })console.log('1', this.state.count) // 0this.setState({ count: this.state.count + 1 })console.log('2', this.state.count) // 0setTimeout(() => {this.setState({ count: this.state.count + 1 })console.log('3', this.state.count) // 2})setTimeout(() => {this.setState({ count: this.state.count + 1 })console.log('4', this.state.count) // 3})} }export default ListDemo import React from 'react'// 函數組件(后面會講),默認沒有 state class StateDemo extends React.Component {constructor(props) {super(props)// 第一,state 要在構造函數中定義this.state = {count: 0}}render() {return <div><p>{this.state.count}</p><button onClick={this.increase}>累加</button></div>}increase = () => {// // 第二,不要直接修改 state ,使用不可變值 ----------------------------// // this.state.count++ // 錯誤// this.setState({// count: this.state.count + 1 // SCU// })// 操作數組、對象的的常用形式// 第三,setState 可能是異步更新(有可能是同步更新) ----------------------------// this.setState({// count: this.state.count + 1// }, () => {// // 聯想 Vue $nextTick - DOM// console.log('count by callback', this.state.count) // 回調函數中可以拿到最新的 state// })// console.log('count', this.state.count) // 異步的,拿不到最新值// // setTimeout 中 setState 是同步的// setTimeout(() => {// this.setState({// count: this.state.count + 1// })// console.log('count in setTimeout', this.state.count)// }, 0)// 自己定義的 DOM 事件,setState 是同步的。再 componentDidMount 中// 第四,state 異步更新的話,更新前會被合并 ----------------------------// // 傳入對象,會被合并(類似 Object.assign )。執行結果只一次 +1// this.setState({// count: this.state.count + 1// })// this.setState({// count: this.state.count + 1// })// this.setState({// count: this.state.count + 1// })// 傳入函數,不會被合并。執行結果是 +3this.setState((prevState, props) => {return {count: prevState.count + 1}})this.setState((prevState, props) => {return {count: prevState.count + 1}})this.setState((prevState, props) => {return {count: prevState.count + 1}})}// bodyClickHandler = () => {// this.setState({// count: this.state.count + 1// })// console.log('count in body event', this.state.count)// }// componentDidMount() {// // 自己定義的 DOM 事件,setState 是同步的// document.body.addEventListener('click', this.bodyClickHandler)// }// componentWillUnmount() {// // 及時銷毀自定義 DOM 事件// document.body.removeEventListener('click', this.bodyClickHandler)// // clearTimeout// } }export default StateDemo// -------------------------- 我是分割線 -----------------------------// // 不可變值(函數式編程,純函數) - 數組 // const list5Copy = this.state.list5.slice() // list5Copy.splice(2, 0, 'a') // 中間插入/刪除 // this.setState({ // list1: this.state.list1.concat(100), // 追加 // list2: [...this.state.list2, 100], // 追加 // list3: this.state.list3.slice(0, 3), // 截取 // list4: this.state.list4.filter(item => item > 100), // 篩選 // list5: list5Copy // 其他操作 // }) // // 注意,不能直接對 this.state.list 進行 push pop splice 等,這樣違反不可變值// // 不可變值 - 對象 // this.setState({ // obj1: Object.assign({}, this.state.obj1, {a: 100}), // obj2: {...this.state.obj2, a: 100} // }) // // 注意,不能直接對 this.state.obj 進行屬性設置,這樣違反不可變值

組件生命周期

單組件生命周期


// React 組件生命周期圖示
// http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

父子組件生命周期,和Vue的一樣

總結

JSX基本使用
條件
列表
事件
表單
組件和props
setState
生命周期

React高級特性

不是每個都很常用,但用到的時候必須要知道
考察候選人對React的掌握是否全面,且有深度
考察做過的項目是否有深度和復雜度(至少能用到高級特性)

函數組件

純函數,輸入props,輸出JSX
沒有實例,沒有生命周期,沒有state
不能擴展其他方法

非受控組件

ref
defaultValue defaultChecked
手動操作DOM元素

import React from 'react'class App extends React.Component {constructor(props) {super(props)this.state = {name: '雙越',flag: true,}this.nameInputRef = React.createRef() // 創建 refthis.fileInputRef = React.createRef()}render() {// // input defaultValue// return <div>// {/* 使用 defaultValue 而不是 value ,使用 ref */}// <input defaultValue={this.state.name} ref={this.nameInputRef}/>// {/* state 并不會隨著改變 */}// <span>state.name: {this.state.name}</span>// <br/>// <button onClick={this.alertName}>alert name</button>// </div>// // checkbox defaultChecked// return <div>// <input// type="checkbox"// defaultChecked={this.state.flag}// />// </div>// filereturn <div><input type="file" ref={this.fileInputRef}/><button onClick={this.alertFile}>alert file</button></div>}alertName = () => {const elem = this.nameInputRef.current // 通過 ref 獲取 DOM 節點alert(elem.value) // 不是 this.state.name}alertFile = () => {const elem = this.fileInputRef.current // 通過 ref 獲取 DOM 節點alert(elem.files[0].name)} }export default App

使用場景

必須手動操作DOM元素,setState實現不了
文件上傳
某些富文本編輯器,需要傳入DOM元素

受控組件 vs 非受控組件

優先使用受控組件,符合React設計原則
必須操作DOM時,再使用非受控組件

Portals

組件默認會按照既定層次嵌套渲染
如何讓組件渲染到父組件以外

import React from 'react' import ReactDOM from 'react-dom' import './style.css'class App extends React.Component {constructor(props) {super(props)this.state = {}}render() {// // 正常渲染// return <div className="modal">// {this.props.children} {/* vue slot */}// </div>// 使用 Portals 渲染到 body 上。// fixed 元素要放在 body 上,有更好的瀏覽器兼容性。return ReactDOM.createPortal(<div className="modal">{this.props.children}</div>,document.body // DOM 節點)} }export default App

使用場景

overflow:hidden
父組件z-index值太小
fixed需要放在body第一層級

context

公共信息(語言、主題)如何傳遞給每個組件
用props太繁瑣
用redux小題大做

import React from 'react'// 創建 Context 填入默認值(任何一個 js 變量) const ThemeContext = React.createContext('light')// 底層組件 - 函數是組件 function ThemeLink (props) {// const theme = this.context // 會報錯。函數式組件沒有實例,即沒有 this// 函數式組件可以使用 Consumerreturn <ThemeContext.Consumer>{ value => <p>link's theme is {value}</p> }</ThemeContext.Consumer> }// 底層組件 - class 組件 class ThemedButton extends React.Component {// 指定 contextType 讀取當前的 theme context。// static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContextrender() {const theme = this.context // React 會往上找到最近的 theme Provider,然后使用它的值。return <div><p>button's theme is {theme}</p></div>} } ThemedButton.contextType = ThemeContext // 指定 contextType 讀取當前的 theme context。// 中間的組件再也不必指明往下傳遞 theme 了。 function Toolbar(props) {return (<div><ThemedButton /><ThemeLink /></div>) }class App extends React.Component {constructor(props) {super(props)this.state = {theme: 'light'}}render() {return <ThemeContext.Provider value={this.state.theme}><Toolbar /><hr/><button onClick={this.changeTheme}>change theme</button></ThemeContext.Provider>}changeTheme = () => {this.setState({theme: this.state.theme === 'light' ? 'dark' : 'light'})} }export default App

異步組件

import()
React.lazy
React.Suspense

import React from 'react'const ContextDemo = React.lazy(() => import('./ContextDemo'))class App extends React.Component {constructor(props) {super(props)}render() {return <div><p>引入一個動態組件</p><hr /><React.Suspense fallback={<div>Loading...</div>}><ContextDemo/></React.Suspense></div>// 1. 強制刷新,可看到 loading (看不到就限制一下 chrome 網速)// 2. 看 network 的 js 加載} }export default App

強制刷新,可看到 loading (看不到就限制一下 chrome 網速)

看 network 的 js 加載,會單獨有個js

性能優化

性能優化,永遠都是面試的重點
性能優化對于React更加重要
回顧講setState時重點強調的不可變值

shouldComponentUpdate(簡稱SCU)


React默認:父組件有更新,子組件無論數據是否改變則無條件也更新
SCU默認返回true

/*** @description 演示 props 和事件* @author 雙越老師*/import React from 'react' import PropTypes from 'prop-types'class Input extends React.Component {constructor(props) {super(props)this.state = {title: ''}}render() {return <div><input value={this.state.title} onChange={this.onTitleChange}/><button onClick={this.onSubmit}>提交</button></div>}onTitleChange = (e) => {this.setState({title: e.target.value})}onSubmit = () => {const { submitTitle } = this.propssubmitTitle(this.state.title) // 'abc'this.setState({title: ''})} } // props 類型檢查 Input.propTypes = {submitTitle: PropTypes.func.isRequired }class List extends React.Component {constructor(props) {super(props)}render() {const { list } = this.propsreturn <ul>{list.map((item, index) => {return <li key={item.id}><span>{item.title}</span></li>})}</ul>} } // props 類型檢查 List.propTypes = {list: PropTypes.arrayOf(PropTypes.object).isRequired }class Footer extends React.Component {constructor(props) {super(props)}render() {return <p>{this.props.text}{this.props.length}</p>}componentDidUpdate() {console.log('footer did update')}shouldComponentUpdate(nextProps, nextState) {if (nextProps.text !== this.props.text|| nextProps.length !== this.props.length) {return true // 可以渲染}return false // 不重復渲染}// React 默認:父組件有更新,子組件則無條件也更新!!!// 性能優化對于 React 更加重要!// SCU 一定要每次都用嗎?—— 需要的時候才優化 }class TodoListDemo extends React.Component {constructor(props) {super(props)// 狀態(數據)提升this.state = {list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}],footerInfo: '底部文字'}}render() {return <div><Input submitTitle={this.onSubmitTitle}/><List list={this.state.list}/><Footer text={this.state.footerInfo} length={this.state.list.length}/></div>}onSubmitTitle = (title) => {this.setState({list: this.state.list.concat({id: `id-${Date.now()}`,title})})} }export default TodoListDemo

SCU一定要配合不可變值

import React from 'react' import PropTypes from 'prop-types' import _ from 'lodash'class Input extends React.Component {constructor(props) {super(props)this.state = {title: ''}}render() {return <div><input value={this.state.title} onChange={this.onTitleChange}/><button onClick={this.onSubmit}>提交</button></div>}onTitleChange = (e) => {this.setState({title: e.target.value})}onSubmit = () => {const { submitTitle } = this.propssubmitTitle(this.state.title)this.setState({title: ''})} } // props 類型檢查 Input.propTypes = {submitTitle: PropTypes.func.isRequired }class List extends React.Component {constructor(props) {super(props)}render() {const { list } = this.propsreturn <ul>{list.map((item, index) => {return <li key={item.id}><span>{item.title}</span></li>})}</ul>}// 增加 shouldComponentUpdateshouldComponentUpdate(nextProps, nextState) {// _.isEqual 做對象或者數組的深度比較(一次性遞歸到底),深度比較較耗費性能,可以使用淺比較,設計state層級盡量設計得不要太深,扁平一些if (_.isEqual(nextProps.list, this.props.list)) {// 相等,則不重復渲染return false}return true // 不相等,則渲染} } // props 類型檢查 List.propTypes = {list: PropTypes.arrayOf(PropTypes.object).isRequired }class TodoListDemo extends React.Component {constructor(props) {super(props)this.state = {list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}]}}render() {return <div><Input submitTitle={this.onSubmitTitle}/><List list={this.state.list}/></div>}onSubmitTitle = (title) => {// 正確的用法this.setState({list: this.state.list.concat({id: `id-${Date.now()}`,title})})// // 為了演示 SCU ,故意寫的錯誤用法,會導致SCU不執行,因為state值push后和setState一致// this.state.list.push({// id: `id-${Date.now()}`,// title// })// this.setState({// list: this.state.list// })} }export default TodoListDemo

SCU使用總結

SCU默認返回true,即React默認重新渲染所有子組件
必須配合“不可變值”一起使用
可先不用SCU,有性能問題時再考慮使用

PureComponent和React.memo

PureComponent,SCU中實現了淺比較
memo,函數組件中的PureComponent
淺比較已適用大部分情況(盡量不要深度比較)

//PureComponentDemo.js import React from 'react' import PropTypes from 'prop-types'class Input extends React.Component {constructor(props) {super(props)this.state = {title: ''}}render() {return <div><input value={this.state.title} onChange={this.onTitleChange}/><button onClick={this.onSubmit}>提交</button></div>}onTitleChange = (e) => {this.setState({title: e.target.value})}onSubmit = () => {const { submitTitle } = this.propssubmitTitle(this.state.title)this.setState({title: ''})} } // props 類型檢查 Input.propTypes = {submitTitle: PropTypes.func.isRequired }class List extends React.PureComponent {constructor(props) {super(props)}render() {const { list } = this.propsreturn <ul>{list.map((item, index) => {return <li key={item.id}><span>{item.title}</span></li>})}</ul>}shouldComponentUpdate() {/*淺比較*/} } // props 類型檢查 List.propTypes = {list: PropTypes.arrayOf(PropTypes.object).isRequired }class TodoListDemo extends React.Component {constructor(props) {super(props)this.state = {list: [{id: 'id-1',title: '標題1'},{id: 'id-2',title: '標題2'},{id: 'id-3',title: '標題3'}]}}render() {return <div><Input submitTitle={this.onSubmitTitle}/><List list={this.state.list}/></div>}onSubmitTitle = (title) => {// 正確的用法this.setState({list: this.state.list.concat({id: `id-${Date.now()}`,title})})// // 為了演示 SCU ,故意寫的錯誤用法// this.state.list.push({// id: `id-${Date.now()}`,// title// })// this.setState({// list: this.state.list// })} }export default TodoListDemo

不可變值immutable.js

徹底擁抱“不可變值”
基于共享數據(不是深拷貝),速度好
有一定學習和遷移成本,按需使用

性能優化-小結

面試重點,且涉及React涉及理念
SCU PureComponent memo immutable.js
按需使用 & state層級

高階組件HOC

關于組件公共邏輯的抽離

mixin,已被React棄用
高階組件HOC
Render Props

import React from 'react'// 高階組件 const withMouse = (Component) => {class withMouseComponent extends React.Component {constructor(props) {super(props)this.state = { x: 0, y: 0 }}handleMouseMove = (event) => {this.setState({x: event.clientX,y: event.clientY})}render() {return (<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>{/* 1. 透傳所有 props 2. 增加 mouse 屬性 */}<Component {...this.props} mouse={this.state}/></div>)}}return withMouseComponent }const App = (props) => {const a = props.aconst { x, y } = props.mouse // 接收 mouse 屬性return (<div style={{ height: '500px' }}><h1>The mouse position is ({x}, {y})</h1><p>{a}</p></div>) }export default withMouse(App) // 返回高階函數


connect源碼

提問:Vue如何實現高階組件?

Renders Props

import React from 'react' import PropTypes from 'prop-types'class Mouse extends React.Component {constructor(props) {super(props)this.state = { x: 0, y: 0 }}handleMouseMove = (event) => {this.setState({x: event.clientX,y: event.clientY})}render() {return (<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>{/* 將當前 state 作為 props ,傳遞給 render (render 是一個函數組件) */}{this.props.render(this.state)}</div>)} } Mouse.propTypes = {render: PropTypes.func.isRequired // 必須接收一個 render 屬性,而且是函數 }const App = (props) => (<div style={{ height: '500px' }}><p>{props.a}</p><Mouse render={/* render 是一個函數組件 */({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>}/></div> )/*** 即,定義了 Mouse 組件,只有獲取 x y 的能力。* 至于 Mouse 組件如何渲染,App 說了算,通過 render prop 的方式告訴 Mouse 。*/export default App

HOC vs Render Props

HOC:模式簡單,但會增加組件層級
Render Props:代碼簡潔,學習成本較高
按需使用

知識點總結

函數組件
非受控組件
Portals
context
異步組件
性能優化(重要)
高階組件HOC
Render Props

Redux

和Vuex作用相同,但比Vuex學習成本高
不可變值,純函數
面試常考

Redux使用

基本概念

store state
action
reducer

//https://www.redux.org.cn/ import { createStore } from 'redux';/*** 這是一個 reducer,形式為 (state, action) => state 的純函數。* 描述了 action 如何把 state 轉變成下一個 state。** state 的形式取決于你,可以是基本類型、數組、對象、* 甚至是 Immutable.js 生成的數據結構。惟一的要點是* 當 state 變化時需要返回全新的對象,而不是修改傳入的參數。** 下面例子使用 `switch` 語句和字符串來做判斷,但你可以寫幫助類(helper)* 根據不同的約定(如方法映射)來判斷,只要適用你的項目即可。*/ function counter(state = 0, action) {switch (action.type) {case 'INCREMENT':return state + 1;case 'DECREMENT':return state - 1;default:return state;} }// 創建 Redux store 來存放應用的狀態。 // API 是 { subscribe, dispatch, getState }。 let store = createStore(counter);// 可以手動訂閱更新,也可以事件綁定到視圖層。 store.subscribe(() =>console.log(store.getState()) );// 改變內部 state 惟一方法是 dispatch 一個 action。 // action 可以被序列化,用日記記錄和儲存下來,后期還可以以回放的方式執行 store.dispatch({ type: 'INCREMENT' }); // 1 store.dispatch({ type: 'INCREMENT' }); // 2 store.dispatch({ type: 'DECREMENT' }); // 1

單項數據流

dispatch(action)
reducer -> newState
subscribe觸發通知
dispatch一個action會觸發reducer,reducer更新狀態,注意不可變值,再觸發訂閱通知,渲染到頁面

react-redux

Provider
connect
mapStateToProps mapDispatchToProps

//index.js import React from 'react' import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App'let store = createStore(todoApp)export default function () {return <Provider store={store}><App /></Provider> } //AddTodo.js import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions'// 函數組件,接收 props 參數 let AddTodo = ({ dispatch }) => {// dispatch 即 props.dispatchlet inputreturn (<div><formonSubmit={e => {e.preventDefault()if (!input.value.trim()) {return}// 創建一個 tododispatch(addTodo(input.value))input.value = ''}}><inputref={node => {input = node}}/><button type="submit">Add Todo</button></form></div>) }// connect 高階組件 ,將 dispatch 作為 props 注入到 AddTodo 組件中 AddTodo = connect()(AddTodo)export default AddTodo //VisibleTodoList.js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList'// 不同類型的 todo 列表 const getVisibleTodos = (todos, filter) => {switch (filter) {case 'SHOW_ALL':return todoscase 'SHOW_COMPLETED':return todos.filter(t => t.completed)case 'SHOW_ACTIVE':return todos.filter(t => !t.completed)} }const mapStateToProps = state => {// state 即 vuex 的總狀態,在 reducer/index.js 中定義return {// 根據完成狀態,篩選數據todos: getVisibleTodos(state.todos, state.visibilityFilter)} }const mapDispatchToProps = dispatch => {return {// 切換完成狀態onTodoClick: id => {dispatch(toggleTodo(id))}} }// connect 高階組件,將 state 和 dispatch 注入到組件 props 中 const VisibleTodoList = connect(mapStateToProps,mapDispatchToProps )(TodoList)export default VisibleTodoList

異步action


異步action使用前要引入中間件

中間件有:
redux-thunk
redux-promise
redux-saga

中間件



Redux知識總結

基本概念
單項數據流
react-redux
異步action
中間件

React-router

面試考點并不多(前提是熟悉React)
路由模式(hash,H5 history),同vue-router
路由配置(動態路由、懶加載),同vue-router

路由模式

hash模式(默認),如http://abc.com/#/user/10
H5 history模式,如http://abc.com/user/20
后者需要server端支持,因此無特殊需求可選擇前者

toC客戶端一般H5 history,toB控制后臺一般hash




React-router總結

路由模式(hash、H5 history)
路由配置(動態路由、懶加載)
掌握基本使用

總結

以上是生活随笔為你收集整理的(七)React使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。