react-hooks/exhaustive-deps inconsistent warning when object destructuring - react-hooks

I use the useHistory hook but I am receiving the exhaustive-deps warning when I don't destructure the object.
const history = useHistory();
useEffect(() => {
history.replace({ search: '' });
}, [history.replace]);
const change = useCallback(() => {
history.replace({ search: '' });
}, [history.replace]);
I receive the following warning:
React Hook useCallback has a missing dependency: 'history'. Either include it or remove the dependency array
But, if I do something like this, it works:
const { replace } = useHistory();
useEffect(() => {
replace({ search: '' });
}, [replace]);
const change = useCallback(() => {
replace({ search: '' });
}, [replace]);

Related

Unable to upload file graphql urql

I got empty object while uploading image. I follow all the step provided, but still can't make it solve. The error said "Variable "$file" got invalid value {}; Upload value invalid."
const client = createClient({
url: import.meta.env.VITE_BASE_API_URL ?? '',
exchanges: [
...defaultExchanges,
dedupExchange,
cacheExchange,
subscriptionExchange({
forwardSubscription: (operation) => ({
subscribe: (sink) => ({
unsubscribe: wsClient.subscribe(operation, sink),
}),
}),
}),
refocusExchange(),
multipartFetchExchange,
],
});
export const UPLOAD_WORKER_IMAGE = gql`
mutation setImage($file: Upload!, $workerId: UUID!) {
updateWorker(input: { patch: { imageUrl: $file }, id: $workerId }) {
}
}
}
`;
// Upload.tsx
const [, mutateWorkerImage] = useMutation<UpdateWorker>(UPLOAD_WORKER_IMAGE);
const file = bindData.avatarFile;
const { error: errorUploadImage, data } = await mutateWorkerImage({
workerId: dataWorker.createWorker.worker.id,
file: file,
});

How to call Redux-toolkit-query Manually on button click

i am using Redux-toolkit-query to fetch data from server. Now i want to call my query on button click,not automatically.I tried this but it's not working.
const { data, refetch } = useGetBuisnessAreasQuery({
enable: false,
refetchOnWindowFocus: false,
manual: true,
refetchOnReconnect: false,
});
You have to use the lazy query hook:
const [ trigger, { data } ] = api.endpoints.getBuisnessAreas.useLazyQuery()
const onClick = () => {
trigger()
}
This is how I did it, it's only cleaner:
in feature/service.ts:
export const authenticationApi = createApi({
reducerPath: 'myApi',
baseQuery: fetchBaseQuery({ baseUrl: baseUrl }),
endpoints: builder => ({
attemptLogin: builder.query({
query: (credentials) => ({
url: '/',
body: JSON.stringify(body)
})
})
})
})
export const { useLazyAttemptLoginQuery } = authenticationApi
and using it:
const [getAuthenticated, { data, isLoading, error }] = useLazyAttemptLoginQuery()

How to use RTK Query in createSlice?

I want to process the data that I get from the request in the slice.
Because not all slices are async (but work with the same data), transformResponse is not suitable.
Is there anything you can suggest?
My code example:
Some RTK Query
export const currencyApi = createApi({
reducerPath: 'currencyApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.apilayer.com/exchangerates_data' }),
endpoints: (build) => ({
fetchCurrencyRates: build.query<IApiResponse, string>({
query: (currency) => ({
url: '/latest',
params: {
base: currency
},
headers: {
apikey: *SomeApiKey*
}
})
})
})
})
Slice where I want to use data from RTK requests
const initialState: ICurrencyState = {
currencyRates: {},
availableCurrencyOptions: [],
fromCurrency: '',
toCurrency: '',
exchangeRate: 0,
error: null
}
export const currencySlice = createSlice({
name: 'currency',
initialState,
reducers: {
//
}
})
Use Hooks in Components
You can send the received data to the slice via useEffect. Something like this:
const { data } = useFetchCurrencyRatesQuery();
useEffect(() => {
if (data !== undefined) {
dispatch(...)
}
}, [data])

AlpineJS can not use magic method $watch

Having the following AlpineJS code and trying to use the magic method $watch, the code will fail with ReferenceError: $watch is not defined
window.experts = {
apiUrl: 'http://test.local:8991/api/',
data: [],
list: [],
expertsForm: null,
expertType: 'all',
queryUrl: '',
currentPage: 1,
sortByName: 'asc',
sortByZip: 'asc',
q: '',
fetchStatus: 'loading...',
retrieveList: () => {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
$watch('specialistType', (value) => console.log(value) );
experts.apiCalls(membersUrl)
},
setExpertType: (type) => {
console.log(type)
},
apiCalls: (url) => {
const response = fetch(url).then(res => {
if (!res.ok) {
experts.fetchStatus = 'error'
}
return res.json()
}).then(result => {
experts.list = result.data;
experts.data = result;
experts.fetchStatus = 'idle'
});
}
}
what goes wrong in this case?
Try accessing it via this. So it should be this.$watch(value, callback).
You should not use arrow function and add this
retrieveList(){
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
this.$watch('specialistType', (value) => console.log(value) );
experts.apiCalls(membersUrl)
},

useState depending on other state

I have this useSiren hook that should update its state with the incoming json argument but it doesnt.
On the first call the json is an empty object, because the fetch effect has not been run yet.
On the second call its also an empty object (triggered by loading getting set to true in App)
And on the third call its filled with valid data. However, the valid data is not applied. The state keeps its initial value.
I guess somehow setSiren must be called to update it, since initial state can only be set once. But how would I do that? Who should call `setSiren?
import { h, render } from 'https://unpkg.com/preact#latest?module';
import { useEffect, useState, useCallback } from 'https://unpkg.com/preact#latest/hooks/dist/hooks.module.js?module';
import htm from "https://unpkg.com/htm#latest/dist/htm.module.js?module";
const html = htm.bind(h);
function useFetch({
method = "GET",
autoFetch = true,
href,
body
}) {
const [loading, setLoading] = useState(false)
const [error, setError] = useState()
const [response, setResponse] = useState()
const [isCancelled, cancel] = useState()
const [json, setJson] = useState({})
const sendRequest = async payload => {
try {
setLoading(true)
setError(undefined)
const response = await fetch(href.replace("http://", "https://"), {
method
})
const json = await response.json()
if (!isCancelled) {
setJson(json)
setResponse(response)
}
return json
} catch (err) {
if (!isCancelled) {
setError(err)
}
throw err
} finally {
setLoading(false)
}
}
if (autoFetch) {
useEffect(() => {
sendRequest(body)
return () => cancel(true)
}, [])
}
return [{
loading,
response,
error,
json
},
sendRequest
]
}
function useSiren(json) {
const [{ entities = [], actions = [], links, title }, setSiren] = useState(json)
const state = (entities.find(entity => entity.class === "state")) || {}
return [
{
title,
state,
actions
},
setSiren
]
}
function Action(props) {
const [{ loading, error, json }, sendRequest] = useFetch({ autoFetch: false, href: props.href, method: props.method })
const requestAndUpdate = () => {
sendRequest().then(props.onRefresh)
}
return (
html`
<button disabled=${loading} onClick=${requestAndUpdate}>
${props.title}
</button>
`
)
}
function App() {
const [{ loading, json }, sendRequest] = useFetch({ href: "https://restlr.io/toggle/0" })
const [{ state, actions }, setSiren] = useSiren(json)
return (
html`<div>
<div>State: ${loading ? "Loading..." : (state.properties && state.properties.value)}</div>
${actions.map(action => html`<${Action} href=${action.href} title=${action.title || action.name} method=${action.method} onRefresh=${setSiren}/>`)}
<button disabled=${loading} onClick=${sendRequest}>
REFRESH
</button>
</div>
`
);
}
render(html`<${App}/>`, document.body)
Maybe what you want to do is to update the siren state when the json param changes? You can use a useEffect to automatically update it.
function useSiren(json) {
const [{ entities = [], actions = [], links, title }, setSiren] = useState(json)
useEffect(() => { // here
setSiren(json)
}, [json])
const state = (entities.find(entity => entity.class === "state")) || {}
return [
{
title,
state,
actions
},
setSiren
]
}
The pattern mentioned by #awmleer is packaged in use-selector:
import { useSelectorValue } from 'use-selector';
const { entities=[], actions=[], title} = json;
const siren = useSelectorValue(() => ({
state: entities.find(entity => entity.class === 'state') || {},
actions,
title
}), [json]);
Disclosure I'm author and maintainer of use-selector

Resources