搭建服務端
yarn add express
yarn add nodemon
在server目錄下 npm init -y
// 增加dev腳本"scripts": {"dev": "nodemon ./index.js"},
引入
git
HOC
- High Order Component 高階組件,是組件的抽象
- HOC不是React提供的API,高級的設計模式
- HOC是一個函數,接收一個組件參數,返回一個新組件
- 普通組件返回的是UI,HOC返回的是一個新組件
- HOC不能修改參數組件,只能傳入組件所需要的props(否則可能會導致參數組價內部的邏輯執行失效)
- HOC是一個沒有副作用的純函數
- HOC除了必須填入被包裹的組件參數以外,其余參數根據需求增加
- HOC不關心數據如何使用,包裹組件不關心數據從哪來,只做渲染
- HOC和包裹組件直接唯一的契合點是props
代碼
完整代碼
git
- index15.jsx
import { fetchListData } from "./index15/front-end/model"
import { listHoc } from './index15/front-end/components/listHoc'import StudentList from './index15/front-end/components/StudentList'
import TeacherList from './index15/front-end/components/TeacherList'const StudentListHoc = listHoc(StudentList, fetchListData)
const TeacherListHoc = listHoc(TeacherList, fetchListData)class App extends React.Component {render() {return (<><StudentListHoc field='student' /><TeacherListHoc field='teacher' /></>)}
}
ReactDOM.render(<App />,document.getElementById('app')
)
- listHoc .jsx
export function listHoc(WrapperComponent, fetchListData) {return class extends React.Component {state = {listData: []}remove = (id) => {this.setState({listData: this.state.listData.filter(item => item.id !== id)})}like = (id) => {this.setState({listData: this.state.listData.map(item => {if (item.id == id) {item.like = Number(item.like) + 1}return item})})}async componentDidMount() {const res = await fetchListData(this.props.field)this.setState({listData: res.data})}render() {return (<>{this.props.field === 'student' ?<WrapperComponentdata={this.state.listData}removeStudent={this.remove}></WrapperComponent>:<WrapperComponentdata={this.state.listData}likeTeacher={this.like}></WrapperComponent>}</>)}}
}
高階組件橫切關注點
- 橫切關注點 → minxins
- 對參數組件本身的邏輯狀態與視圖的橫向切割
- 讓HOC來完成邏輯和狀態的管理
- 讓參數組件來完成視圖的渲染
- 讓HOC將數據與邏輯傳遞到參數組件中,從而完成關注點分離且有機結合的任務
柯里化
- index.jsx
const StudentListHoc = listHoc(StudentList)(fetchListData)
const TeacherListHoc = listHoc(TeacherList)(fetchListData)
- listHoc .jsx
export function listHoc(WrapperComponent) {return function (fetchListData) {return class extends React.Component {......}}
}
還可以在
StudentList
子組件內部返回listHoc(StudentList)
注意事項
有value就必須有onChange,否則使用defaultValue
- 使用剩余參數去排除不需要的屬性
- 對“HOC不能修改參數組件”的理解,例如,如下修改參數組件的生命周期函數
- MyInput.jsx
export default class MyInput extends React.Component {componentDidUpdate() {console.log('MyInput更新')}render() {return (<div><h1>{this.props.inputValue}</h1><p>總計:{this.props.b + this.props.c}</p><inputtype="text"placeholder="請填寫"value={this.props.inputValue}onChange={this.props.valueInput}/></div>)}
}
- InputHoc
export function InputHoc(WrapperComponent) {// 注意 如果在這里修重寫生命周期函數componentDidUpdate// WrapperComponent內的componentDidUpdate將不會被觸發// WrapperComponent.prototype.componentDidUpdate = () => {// console.log('InputHoc內重寫componentDidUpdate')// }return class InputHocComponent extends React.Component {componentDidUpdate() {console.log('不重寫,都觸發')}state = {inputValue: ''}valueInput = (e) => {this.setState({inputValue: e.target.value})}render() {// 排除參數組件中不需要的屬性// 注意 剩余參數時變量名必須叫propsconst { a, ...props } = this.propsreturn (<WrapperComponentinputValue={this.state.inputValue}valueInput={this.valueInput}{...props}/>)}}
}
- index.jsx
import MyInput from './index16/front-end/components/MyInput'
import { InputHoc } from './index16/front-end/components/InputHoc'
const MyInputHoc = InputHoc(MyInput)class App extends React.Component {state = {a: 1,b: 2,c: 3}render() {return (<><MyInputHoc {...this.state} /></>)}
}
ReactDOM.render(<App />,document.getElementById('app')
)
- MyInput使用函數組件
export default function MyInput(props) {return (<div><h1>{props.inputValue}</h1><p>總計:{props.b + props.c}</p><inputtype="text"placeholder="請填寫"value={props.inputValue}onChange={props.valueInput}/></div>)
}