导航
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该两数就是高阶函数
常见的高阶函数有:Promise、 setTimeout、arr.map() 等等
函数的柯里化:通过函数的调用,继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式。
使用场景/总结:
具体代码地址: https://github.com/evestorm/learning-js/blob/master/React/React本尊/learning/base/js/23高阶组件/App.jsx
components/listHOC.jsx
/**
* @param WrapperComponent 要渲染的 React 组件
* @param fetchListData 请求数据的方法
*/
function listHOC(WrapperComponent, fetchListData) {
return class extends React.Component {
// 存储数据
state = {
listData: []
}
async componentDidMount() {
// 根据传参,获取数据
const result = await fetchListData(this.props.field);
this.setState({
listData: result.data
})
}
// 管理数据
removeStudent = (id) => {
this.setState({
listData: this.state.listData.filter(v => v.id !== id),
})
}
likeTeacher = (id) => {
this.setState({
listData: this.state.listData.map(v => {
v.id === id ? v.like++ : '';
return v;
})
})
}
render() {
// 把数据传递给组件
return (
<>
{
this.props.field === 'student'
? <WrapperComponent data={ this.state.listData } removeStudent={ this.removeStudent.bind(this) } />
: <WrapperComponent data={ this.state.listData } likeTeacher={ this.likeTeacher.bind(this) } />
}
</>
)
}
}
}
export default listHOC
AppHOC.jsx
// 改造后
import StudentList from './components/StudentList';
import TeacherList from './components/TeacherList';
import { fetchListData } from './model/index';
import listHOC from './components/listHOC';
// 返回新的组件
const StudentListHoc = listHOC(StudentList, fetchListData);
const TeacherListHoc = listHOC(TeacherList, fetchListData);
class App extends React.Component {
// state = {
// studentList: [],
// teacherList: []
// }
// async componentDidMount() {
// const studentList = await fetchListData('student');
// const teacherList = await fetchListData('teacher');
// this.setState({
// studentList: studentList.data,
// teacherList: teacherList.data
// })
// }
// removeStudent = (id) => {
// this.setState({
// studentList: this.state.studentList.filter(v => v.id !== id),
// })
// }
// likeTeacher = (id) => {
// this.setState({
// teacherList: this.state.teacherList.map(v => {
// v.id === id ? v.like++ : '';
// return v;
// })
// })
// }
render() {
return (
<div className="app">
{/* <StudentList data={ this.state.studentList } removeStudent={ this.removeStudent.bind(this) } />
<TeacherList data={ this.state.teacherList } likeTeacher={ this.likeTeacher.bind(this) } /> */}
<StudentListHoc field="student" />
<TeacherListHoc field="teacher" />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
改造 listHOC.jsx 使其成为柯里化函数
function listHOC(WrapperComponent) {
// 返回一个函数,柯里化
return function(fetchListData) {
return class extends React.Component {
state = {
listData: []
}
async componentDidMount() {
const result = await fetchListData(this.props.field);
this.setState({
listData: result.data
})
}
removeStudent = (id) => {
this.setState({
listData: this.state.listData.filter(v => v.id !== id),
})
}
likeTeacher = (id) => {
this.setState({
listData: this.state.listData.map(v => {
v.id === id ? v.like++ : '';
return v;
})
})
}
render() {
return (
<>
{
this.props.field === 'student'
? <WrapperComponent data={ this.state.listData } removeStudent={ this.removeStudent.bind(this) } />
: <WrapperComponent data={ this.state.listData } likeTeacher={ this.likeTeacher.bind(this) } />
}
</>
)
}
}
}
}
export default listHOC
对 StudentList.jsx 进行改造,返回一个 listHOC 返回的函数
import listHOC from "./listHOC2";
class StudentList extends React.Component {
render() {
return (
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Grade</th>
<th>tools</th>
</tr>
</thead>
<tbody>
{
this.props.data.map((item) => {
return (
<tr key={ item.id }>
<td>{ item.id }</td>
<td>{ item.name }</td>
<td>{ item.grade }</td>
<td>
<button
onClick={ () => this.props.removeStudent(item.id) }
>删除</button>
</td>
</tr>
)
})
}
</tbody>
</table>
)
}
}
export default listHOC(StudentList)
改造后的 App.jsx
// 改造后:HOC + 柯里化
import StudentList from './components/StudentList2';
import TeacherList from './components/TeacherList2';
import { fetchListData } from './model/index';
const StudentListHoc = StudentList(fetchListData);
const TeacherListHoc = TeacherList(fetchListData);
class App extends React.Component {
// state = {
// studentList: [],
// teacherList: []
// }
// async componentDidMount() {
// const studentList = await fetchListData('student');
// const teacherList = await fetchListData('teacher');
// this.setState({
// studentList: studentList.data,
// teacherList: teacherList.data
// })
// }
// removeStudent = (id) => {
// this.setState({
// studentList: this.state.studentList.filter(v => v.id !== id),
// })
// }
// likeTeacher = (id) => {
// this.setState({
// teacherList: this.state.teacherList.map(v => {
// v.id === id ? v.like++ : '';
// return v;
// })
// })
// }
render() {
return (
<div className="app">
{/* <StudentList data={ this.state.studentList } removeStudent={ this.removeStudent.bind(this) } />
<TeacherList data={ this.state.teacherList } likeTeacher={ this.likeTeacher.bind(this) } /> */}
<StudentListHoc field="student" />
<TeacherListHoc field="teacher" />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
App注意事项.jsx
import MyInput from "./components/MyInput";
import InputHOC from "./components/InputHOC";
const MyInputHOC = InputHOC(MyInput);
class App extends React.Component {
state = {
a: 1,
b: 2,
c: 3
}
render() {
return (
<div className="app">
{/* 给 HOC 传入全部参数 */}
<MyInputHOC { ...this.state } />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
InputHOC.jsx
function InputHOC(WrapperComponent) {
class InputHocComponent extends React.Component {
state = {
inputValue: ''
}
valueInput(e) {
this.setState({
inputValue: e.target.value
})
}
render() {
// 如何排除参数组件不需要的属性
// MyInput 组件除了 a 属性都需要,所以可以用剩余参数传递
const { a, ...props } = this.props;
return (
<WrapperComponent
inputValue={ this.state.inputValue }
valueInput={ this.valueInput.bind(this) }
{ ...props }
/>
)
}
}
InputHocComponent.displayName = 'InputHOC';
return InputHocComponent;
}
export default InputHOC;
MyInput.jsx
例如组件内部写了 componentDidUpdate 方法
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>
<input type="text"
value={ this.props.inputValue }
onChange={ this.props.valueInput }
/>
</div>
)
}
}
InputHOC.jsx
然后在 HOC 中通过原型的方式覆盖了组件内部的 componentDidUpdate
function InputHOC(WrapperComponent) {
// 会覆盖 MyInput 组件内的 componentDidUpdate
// 因为这相当于修改了 MyInput 的 componentDidUpdate 方法
// 所以得写在新返回的 InputHocComponent 组件里边去,因为这是个新组件,不会与 WrapperComponent 干扰
// 所以:高阶组件不能修改参数组件,可能会导致参数组件内部的逻辑的执行失效
// WrapperComponent.prototype.componentDidUpdate = function() {
// console.log('InputHOC: 我更新了');
// }
class InputHocComponent extends React.Component {
state = {
inputValue: ''
}
// 这样写就行,组件内和HOC的 componentDidUpdate 都能执行
// 所以:一切的功能可以在组件内实现
componentDidUpdate() {
console.log('InputHOC: 我更新了');
}
valueInput(e) {
this.setState({
inputValue: e.target.value
})
}
render() {
// 如何排除参数组件不需要的属性
// MyInput 组件除了 a 属性都需要,所以可以用剩余参数传递
const { a, ...props } = this.props;
return (
<WrapperComponent
inputValue={ this.state.inputValue }
valueInput={ this.valueInput.bind(this) }
{ ...props }
/>
)
}
}
InputHocComponent.displayName = 'InputHOC';
return InputHocComponent;
}
export default InputHOC;
使用函数组件实现之前的 MyInput.jsx
// 使用 函数组件 实现 MyInput 类组件
function MyInput(props) {
// 这样写,就能在组件更新时,调用 log
React.useEffect(() => {
console.log('MyInput组件内部: 我更新了');
}, [props.inputValue]);
return (
<div>
<h1>{ props.inputValue }</h1>
<p>总计: { props.b + props.c }</p>
<input type="text"
value={ props.inputValue }
onChange={ props.valueInput }
/>
</div>
)
}
export default MyInput;