我正在尝试确定我是否正在使用 RTKQuery 来使我的 Stencil.js 组件的状态与数据库正确同步。
我的行为是我的组件将使用 RTK 查询和 store.dispatch() 获取数据并将其分配给本地状态。然后用户改变组件,这也是通过 store dispatch() 函数使用 rtk 查询 api 发出的请求。
我设法让我的组件重新呈现的唯一方法是使用
componentWIllLoad()
生命周期方法订阅商店并传入一个获取函数 store.dispatch(api.endpoints.fetchFunction.initiate())
作为回调。
虽然这可以很好地保持状态同步,但它确实会导致 fetchFunction() 之间的无限调用循环,该 fetchFunction() 作为动作分派并调用订阅,然后调用 fetchFunction() 等等。这可以通过订阅中的一个简单的 console.log() 语句看出。
虽然这种行为不是世界末日,但感觉不是很优雅。可以改进吗?
RTK 查询设置: 我有一个 API:
- api.ts
export const oracleApi = createApi({
reducerPath: 'oracleApi',
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:8000/api/v1/',
prepareHeaders: async headers => {
try {
console.log(await localForage.getItem('CHLJWT'))
const token = await getToken(localForage)
if (token) {
headers.set('Authorization', `CHLJWT ${token.access}`)
}
console.log('HEADERS Authorization: ', headers.get('Authorization'))
return headers
} catch (error) {
console.error('Login Required: ', error)
}
},
}),
tagTypes: ['Spaces', 'Auth', 'Users', 'Documents', 'Figures', 'Organisations'],
endpoints: build => ({
//Auth
login: build.mutation<CHLTokenData, CHLLoginData>({
query(body) {
return {
url: `auth/jwt/create/`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Auth', id: 'LIST' }],
}),
redux 商店:
- store.ts
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[api.reducerPath]: api.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(api.middleware),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
和一个 index.ts 文件来组合它们
-index.ts
export const api = {
//Spaces
getSpace: async (id: SpaceId) => {
try {
const space = await store.dispatch(api.endpoints.getSpace.initiate(id))
return space
} catch (error) {
console.error(error)
}
},
getSpaces: async (data?) => {
try {
const spaces = await store.dispatch(api.endpoints.getSpaces.initiate())
return spaces
} catch (error) {
console.error(error)
}
},
deleteSpace: async (id: SpaceId) => {
try {
await store.dispatch(api.endpoints.deleteSpace.initiate(id))
} catch (error) {
console.error(error)
}
},
createSpace: async data => {
try {
const res = await store.dispatch(api.endpoints.addSpace.initiate(data))
return res
} catch (error) {
console.error(error)
}
},
updateSpace: async (space, data) => {
try {
const id = space.id
const res = await store.dispatch(api.endpoints.updateSpace.initiate({ id, ...data }))
return res
} catch (error) {
console.error(error)
}
},
}
最后,我有一个 stencil.js 组件
import { store } from 'server_state/store'
import { api } from 'server_state/index'
@Component({
tag: 'app-topbar',
styleUrl: 'app-topbar.css',
})
export class AppTopbar {
private unsubscribe: () => void
@State() space: Space
async componentWillLoad() {
this.spaceId = Router.activePath.slice(8, 44) as SpaceId
this.unsubscribe = store.subscribe(async () => {
await this.loadData()
})
await this.loadData()
}
disconnectedCallback() {
this.unsubscribe()
}
async loadData() {
try {
console.log('Loading data:app-topbar')
api.getSpace(this.spaceId)
this.space = spaceResult.data
} catch (error) {
console.error(error)
}
}
render() {
///
}
}
随着改进这个模式,我特别感兴趣的是是否可以使用 redux 的 createApi 来获取数据而不调用 store.subscribe() 回调。
谢谢!
这真的分为3个问题:
对于前两个主题,请参阅我的博客文章React-Redux 的历史和实现 中的长篇解释和谈话深入研究 React-Redux,但 TL;DR 可以在我们的文档中看到 https://redux.js.org/tutorials/fundamentals/part-5-ui-react#integrating-redux-with-a-ui:
// 1) Create a new Redux store
const store = configureStore({reducer: counterReducer})
// 2) Subscribe to redraw whenever the data changes in the future
store.subscribe(render)
// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')
// 3) When the subscription callback runs:
function render() {
// 3.1) Get the current store state
const state = store.getState()
// 3.2) Extract the data you want
const newValue = state.value.toString()
// 3.3) Update the UI with the new value
valueEl.innerHTML = newValue
}
// 4) Display the UI with the initial store state
render()
与 Redux 集成的每个 UI 层都需要执行相同的基本操作:订阅、获取最新状态、此组件所需的差异值、如果这些值发生变化则强制重新绘制以及必要的更新。
对于 React,我们将所有逻辑封装在 React-Redux 包和我们的
useSelector
钩子(以及旧的 connect
包装器)中。 RTK Query 的 React hooks 像 useGetPokemonQuery
建立在它之上。
对于 Stencil,您需要从使用任何与 React-Redux 等效的东西开始。我看到在 https://stenciljs.com/docs/stencil-redux 已经有一个讨论使用 Redux 的 Stencil 文档页面,并且有一个
@stencil/redux
包。
与 Redux 和 RTK 的其余部分一样,RTK 查询也是 UI 不可知的。所以,你可以在没有 React 的情况下使用它,但是你必须做更多的工作。
我们在文档中介绍了一些关键部分:
在这种情况下,您可能希望根据
const selector = api.endpoints.getPokemon.select("pikachu")
生成端点选择器,并将其传递给 Stencil 包装器 mapStateToProps
以从存储中选择该数据。假设 @stencil/redux
做我认为它做的事,那 should 触发组件中的更新。