记录一下这阵子写react时 Apollo踩下的坑
背景: 项目使用 creat-react-app 搭建脚手架, 技术栈GraphQL+Apollo+React ,所以下面这几个包少不了
apollo-boost
: Package containing everything you need to set up Apollo Clientreact-apollo
: View layer integration for Reactgraphql-tag
: Necessary for parsing your GraphQL queriesgraphql
: Also parses your GraphQL queries
说明白了 GraphQL 和数据库本身并没有直接关系,但相当于平时写的 Get Post请求
这里主要用 react-apollo 的两个核心组件: Query
Mutation
Query
坑
Apollo 的<Query></Query> 查询组件只能在render() 里面执行,而在react中 render() 顾名思义--渲染 是不能修改组件的state 也就是说 setState是用不了了,那么我有个问题 如果获取数据之后,传入子组件那么
1. 子组件的操作 如何修改状态?
2. 子组件修改状态之后,父组件如何刷新数据?
const IMAGE_ORDER_LIST = gql`
query OrderListQuery($status:Int, $page: Int, $priceSort: String, $keyword: String, $time: String) {
OrderListQuery(status: $status, page: $page ,priceSort: $priceSort, keyword: $keyword, time: $time) {
items {
order_num
id
price
image_url
status_msg
created_on
}
paginate {
current_page
last_page
}
}
}`
class Parent extends React.Component {
render() {
<Query query={IMAGE_ORDER_LIST}>
{({ loading, error, data }) => {
if (loading) return //do something
if (error) return //do something
return (
<div>
<ChildA orders={data.xx} ></ChildA>
<ChildB orders={data.aa} ></ChildB>
<ChildC orders={data.ss} ></ChildC>
</div>
)
}}
</Query>
}
}
折腾了好久,最后找到暂时解决的办法,记录下
问题1
问题大致还原成: 实现 单选 or 全选订单,并且进行 驳回 or 通过操作 传送门 ->
想过的点
- 想过父组件传递函数给checkbox组件里面更改自身状态,但是更改后的状态如何传递给爷爷组件.. 这就尴尬了
- 在爷、 父 、子组件中,将操作逻辑放置 父组件,但是三个组件的通讯问题 又成了问题,不会要用redux吧...
后来找到例子: https://codesandbox.io/s/j4krmvw4ky 实际上是用 状态提升方法 由此想到思路, 把 Parent 组件中的 ChildA 、 ChildB 、 ChildC 重新封装起来成 BigChild组件,然后将Parnet 获取到的数据传入 BigChild转化为state,这样就可以更改状态啦~
//Parent .js
class Parent extends React.Component {
render() {
<Query query={IMAGE_ORDER_LIST}>
{({ loading, error, data }) => {
if (loading) return //do something
if (error) return //do something
return (
<div>
<BigChild orders={data} ></BigChild>
</div>
)
}}
</Query>
}
}
//BigChild .js
class BigChild extends React.Component {
constructor(props) {
super(props)
this.state = {
data: this.props.orders
}
}
render() {
let { data } = this.state
return (
<div>
<ChildA orders={data} ></ChildA>
<ChildA orders={data} ></ChildA>
<ChildA orders={data} ></ChildA>
</div>
)
}
}
问题2
问题1解决了,订单操作完成之后,重新进入Parent 页面之后 render() 还是修改之前的数据
查了文档,发现apollo是有缓存机制的
By default your component will try to read from the cache first, and if the full data for your query is in the cache then Apollo simply returns the data from the cache 【Click to read me!】
而apollo自带两种刷新数据方法是 pollInterval 以及 refetch
pollInterval
顾名思义 轮询,类似setInterVal(), 周期可以作为参数 自定义,使用方法是 pollInterval ={ time }
//500毫秒查询一次
<Query
query={IMAGE_ORDER_LIST}
pollInterval={500}
notifyOnNetworkStatusChange //一定要开启,监控状态码
>
{({ loading, error, data, startPolling, stopPolling }) => {
//loading error就似乎没用了
if (networkStatus === 7) {
stopPolling() //停止轮询
//加载完成
} else if (networkStatus < 7) {
return //loading
} else if (networkStatus === 8) {
return //出错
}
}}
</Query>
状态码:
-
loading:
该查询从未运行过,现在请求正在等待 -
setVariables:
查询的变量发生变化 -
fetchMore:
表示fetchMore在此查询上调用了该命令,并且创建的网络请求当前正在运行 -
refetch:
这意味着refetch已在查询中调用并且重新获取请求当前正在运行。 - ...略
-
poll:
表示轮询查询当前正在运行 -
ready:
加载完成 -
error:
错误
refetch
这个非常实用,如果在组件内更新,或者点击事件之类 更新数据,直接参考说明文档例子,点击即可实现。
const DogPhoto = ({ breed }) => (
<Query
query={GET_DOG_PHOTO}
variables={{ breed }}
skip={!breed}
>
{({ loading, error, data, refetch }) => {
if (loading) return null;
if (error) return `Error!: ${error}`;
return (
<div>
<img
src={data.dog.displayImage}
style={{ height: 100, width: 100 }}
/>
<button onClick={() => refetch()}>Refetch!</button>
</div>
);
}}
</Query>
);
看上去非常实用,但是我的这个问题: 当订单操作完成 重新进入parent组件页面,再立即执行refetch() 似乎不太可能,如果直接添加 refetch()
{({ loading, error, data, refetch }) => {
refetch()
render (
//return something
)
}}
会发现 页面一直在refetch 使用了stopPolling 也不会停止, 后来使用 订单操作完成返回一个参数给 parent页面,parent页面根据参数判断 是否进行refetch 同样也不行!
另外想到 添加一个隐藏button 让页面进入的时候自执行,结果页面抽搐了...
所以只能放弃 refetch() 这个好看但用不着的方法... 难受
最后
用 pollInterval
根据状态做了一些改善
//500毫秒查询一次
render() {
let loadNum = 0
return (
<Query
query={IMAGE_ORDER_LIST}
pollInterval={100}
notifyOnNetworkStatusChange //一定要开启,监控状态码
>
{({ loading, error, data, startPolling, stopPolling }) => {
loadNum++ //轮询时候增加
if (networkStatus === 7 && loadNum > 3) {
stopPolling() //停止轮询
//加载完成 do something
} else if (networkStatus <= 7) {
return //loading
} else if (networkStatus === 8) {
return //出错
}
}}
</Query>
)
}
大功告成,还有其他的解决方法 还望大神指导!
网友评论