export default function(props)
{
return props.loading ? <div className="loader" /> :
finishdiv>;
}
完整演示:https://codesandbox.io/s/k39472w027。
注:上面两段代码你可能会想,为什么 Func
和 Class
都能实现一个组件,他们有什么差别吗?
其实你在开发时不容易感觉到差别,但 React 本身是进行了很多差别处理,如果是 Class 类,React 会用 new
关键字实例化,然后调用该实例的 render
方法,如果是 Func 函数,React 会直接调用它。
Refs
Refs:https://reactjs.org/docs/refs-and-the-dom.html
如果你是一个 jQuery 转型 React 的开发,会很自然的想到,我找到 Loading 组件的节点,控制他的显示与隐藏,当然这也是可以的,React 提供 Refs 方便你访问 DOM 节点 或 React 元素。
export default class extends Component {
componentDidMount() {
fetch().then(() => {
this.el.changeLoading(false);
});
}
render() {
return (
<Loading ref={el => { this.el = el; }} />
);
}
}
完整演示:https://codesandbox.io/s/ywwmm3j46z。
通用逻辑抽离
当你的应用做到一定的复杂度,不同的页面都会有 loading 效果,你肯定不希望每个页面都重复的书写一样的逻辑,这样会导致你的代码重复且混乱。
React 中有两个比较常见的解决方案 HOC
和 RenderProps
,其实这两个这两个概念都是不依赖 React 的。
让我们暂时忘掉 React,下面我对 HOC
和 RenderProps
写两个例子,你会发现组件复用是如此简单。
HOC
HOC:https://reactjs.org/docs/higher-order-components.html
HOC 其实就是一种装饰器模式,它接受一个组件作为参数,然后返回相同的组件,这样就可以额外增加一些功能。
const func = () => {
console.log("func");
};
const
wrap = func => {
console.log("wrap");
return func;
};
// wrap 逻辑已被复用
wrap(func)();
完整演示:https://codesandbox.io/s/8zx85nrzk2。
Render Props
Render Props:https://reactjs.org/docs/render-props.html
Render Props 就是我们给一个函数传递一个回调函数做为参数,该回调函数就能利用外面函数的执行结果做为参数,执行任何操作。
const func = param => {
console.log("func");
};
const wrap = (param, func) => {
console.log("wrap");
func(param);
};
// wrap 逻辑已被复用
wrap("", func);
完整演示:https://codesandbox.io/s/0v1p4rp7xv。
相同点:
不同点:
总的来说,在需要复用组件逻辑的时候,我个人更倾向于 Render Props 的方式。
复杂状态管理
当你的应用越来越大,组件之间交互越来越复杂,那整个页面的数据逻辑将变得难以管理,这时候为了方便管理应用的状态,你可以选择一些状态管理工具,例如 Redux(https://github.com/reduxjs/redux)、Flux(https://github.com/facebook/flux)、dva(https://github.com/dvajs/dva) 等。
Redux
Redux:https://redux.js.org/
我不太想谈这些数据流框架,因为他们的概念 action
、 store
、 dispatch
太过于生涩难懂。
现代前端框架 React 和 Vue 其实都是一个套路,通过数据渲染试图,然后视图上操作反过来更新数据,重新渲染视图,刷新页面。
数据叫做 store
,动作叫做 ation
,触发行为叫 dispatch
,然后数据到视图的渲染由 React/Vue 处理的。
// reducers.js
const initialState = {
loading: false
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case "CHANGE_LOADING":
return {
loading: action.payload
};
default:
return state;
}
}
完整演示:https://codesandbox.io/s/94zoy50q6w。
Saga
Saga:https://redux-saga.js.org/
当你代码中有大量的异步操作时,例如 fetch 请求,你肯定会想到 事件监听
、 回调函数
、 发布/订阅
。
很好,上一个例子其实就是 事件监听
的处理方式,然后 回调函数
的主流的解决方案是 redux-thunk(https://github.com/reduxjs/redux-thunk),而 发布/订阅
的主流解决方案是 saga。
import { takeLatest, put } from "redux-saga/effects";
import fetch from "./fetch";
function* fetchInfo(action) {
yield put({
type: "CHANGE_LOADING",
payload: true
});
yield fetch();
yield put({
type: "CHANGE_LOADING",
payload: false
});
}
export default function* fetchSaga() {
yield takeLatest("FETCH_REQUEST", fetchInfo);
}
完整演示:https://codesandbox.io/s/rrnp9vk3wp。
当你耐心看到这里,我知道你对 React 肯定有一定的经验,现在还可以做很多,例如把 loading 状态提升到 Store 的顶部,那整个站点就只有一个 loading 了,然后你还可以将 fetch 再封装一个 HOC 修改 loading 状态,这就是一个相对完美的 loading,其实 React 业务开发都可以用这个套路。
新的 API
Context
Context:https://reactjs.org/docs/context.html

上面 redux 的例子是不是过于复杂。
对于简单的业务,虽然有很多页面,嵌套层次也很复杂,你当然可以不用状态管理工具,你可以试着使用 Context,它可以方便你传递数据,它其实就是 Render Props 的一种实现。
export