Redux toolkit createAsyncThunk using Parameter - redux-toolkit

I need your help.
We implemented createAsyncThunk using the Redux Toolkit.
However, as shown in the picture, I need a function to change ChannelId flexibly. Can't I use the parameter in createAsyncThunk?
How can I use it if I can?
Or is there any other way?
I am sorry that the quality of the question is low because I am Korean using a translator.
enter image description here
// ACTION
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (data, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
// SLICE
const youtube_PlaylistSlice = createSlice({
name: "YOUTUBE_PLAYLIST",
initialState,
reducers: {},
// createAsyncThunk 호출 처리 = extraReducers
extraReducers(builder) {
builder
.addCase(getYoutubeList_PlayList.pending, (state, action) => {
state.loading = true;
})
.addCase(getYoutubeList_PlayList.fulfilled, (state, action: PayloadAction<youtubeResponse>) => {
state.loading = false;
state.data = action.payload;
})
.addCase(getYoutubeList_PlayList.rejected, (state, action: PayloadAction<any>) => {
state.error = action.payload;
});
},
});

You named both the incoming argument data as well as the result of your axios call. That will "shadow" the original data and you cannot access it any more. Give those two variables different names.
Here I called it arg, which allows you to access arg.channelId.
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (arg, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${arg.channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
You would now dispatch this as dispatch(getYoutubeList_PlayList({ channelId: 5 }))

Related

Problems calling redux toolkit action (slice) from Saga

I am wondering if it is possible to dispatch an action defined in a createSlice from a saga (the action is indeed exported).
The action I want to dispatch is defined in a separate module and looks like this:
// Create reducer and actions
const rtkSlice = createSlice({
name: "picklistAndMaterialsModule",
initialState,
reducers: {
saveList: (
state,
action: PayloadAction<{ picklists: any }>
) => {
const { picklists } = action.payload;
console.log({ picklists });
},
},
});
I am struggeling to call this saveList action from the saga. My attempt looks like this
function* updateApprovedOperations({ payload }) {
try {
yield put(actions.updateApprovedOperationRequested());
const { approvedOperations } = payload; // list of all approved from the payload, including all properties
const result = yield call(_updateApprovedOperations, payload);
const { data } = result;
const { operations, picklists } = data; // response, not including all properties
yield all([
put(nextDayPickListsActions.updatePickListBatch({ data: picklists })),
put(operationActions.batchUpdate({ operations: updatedOperations })),
put(saveList({ data: picklists })), // HERE IS THE SLICE ACTION, IT IS NOT BEING CALLED
]);
however, saveList is not being called.... Can anyone see why it is not working?

How can i make the interceptor run a function on error exept for one specific request?

this is my interceptor:
axios.interceptors.response.use(
(response) => {
if (error.response?.status === 403) {
unstable_batchedUpdates(() => {
// to force react state changes outside of React components
useSnackBarStore.getState().show({
message: `${i18n.t('forbidden')}: ${error.toJSON().config.url}`,
severity: 'error',
})
})
}
return Promise.reject(error)
}
)
I want this behavior all the time except when I make this specific call or at least except every head call
export const companiesQueries = {
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await axios.head(`/companies/${companyId}`)
},
},
fixed by applying these changes to the api call:
const uninterceptedAxiosInstance = axios.create()
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await
uninterceptedAxiosInstance.head(`/companies/${companyId}`)
},
}

Failed to add new elements when set initialState as an empty object

I try to use redux toolkit and I have this as menu-slice.js
I try to use property accessors to add a new property to fileItems, its initial value is an empty object.
import { createSlice } from "#reduxjs/toolkit";
const menuSlice = createSlice({
name: "ui",
initialState: {
fileItems: {},
},
reducers: {
setFileDate: (state, action) => {
state.FileDate = action.payload;
},
replaceFileItems: (state, action) => {
const filesList = action.payload.map((fileName) =>
fileName.slice(fileName.indexOf("/") + 1)
);
state.fileItems[state.FileDate] = filesList;
console.log(`filesList: ${filesList}`);
console.log(`state.fileItems: ${JSON.stringify(state.fileItems)}`);
console.log(`state.FileDate: ${state.FileDate}`);
state.fileContents = null;
},
I call dispatch with the api return value ( dispatch(menuActions.replaceFileItems(fileResponse.data));)
in menu-action.js:
the return value is an array of strings.
export const fetchFiles = (fileDate) => {
return async (dispatch) => {
const fetchFilesList = async () => {
const response = await fetch(
"some url" +
new URLSearchParams({
env: "https://env.com",
date: fileDate,
})
);
if (!response.ok) {
throw new Error("Fail to fetch files list!");
}
const data = await response.json();
return data;
};
try {
const fileResponse = await fetchFilesList();
dispatch(menuActions.setFileDate(FileDate));
dispatch(menuActions.replaceFileItems(fileResponse.data));
} catch (error) {
dispatch(
menuActions.showNotification({....
})
);
}
};
};
But it never prints console logs and didn't display where went wrong in the console or in the chrome redux extension.
I want to add data into state.fileItems on each click that triggers fetchFiles() when it returns a new array:
from state.fileItems = {}
check if state.fileItems already has the date as key,
if not already has the date as key,
change to ex: state.fileItems = {"2022-01-01": Array(2)}
and so on..
ex: state.fileItems = { "2022-01-01": Array(2), "2022-01-02": Array(2) }
I also tried to set state.fileItems as an empty array, and use push, but it didn't work either, nothing printed out, state.fileItems value was always undefined.
Can anyone please tell me why this didn't work?
Thanks for your time to read my question.

Curious why we can't get at the args in a query, in the onSuccess?

So, I have some ancilliary behaviors in the onSuccess, like analytics and such. And I need to pass in to the tracking, not only the result of the query/mutation (mutation in this case), BUT also an arg I passed in. Seems I can only do it if I attach it to the return "data"?
export default function useProductToWishList () {
const queryClient = useQueryClient();
return useMutation(
async ({ product, email }) => {
const data = await product.addWishList({ product, email });
if (data.status === 500 || data.err) throw new Error(data.err);
return data;
},
{
onSuccess:(data) => {
const { product: response = {} } = data?.data ?? {};
queryClient.setQueryData(['products'], {...response });
analytics(response, email); // HERE. How can I get at email?
}
}
)
}
seems odd to do, when I don't need it for the response, but for a side effect. Any thoughts?
return { ...data, email }
for useMutation, the variables are passed as the second argument to onSuccess. This is documented in the api docs. So in your example, it's simply:
onSuccess: (data, { product, email }) =>

Why TextDocumentContentProvider dont call provideTextDocumentContent on update when query params changes?

as title says, when i wanna update TextDocumentContentProvider with different query params by calling update method provideTextDocumentContent is not called...
only way i managed to get it working was with same URI as in calling
vscode.commands.executeCommand('vscode.previewHtml', URI, 2, 'Storybook');
relevant part of code:
// calculates uri based on editor state - depends on actual caret position
// all uris will start with 'storybook://preview'
function getPreviewUri(editor: vscode.TextEditor): vscode.Uri;
// transforms uri, so web server will understand
// ex: 'storybook://preview?name=fred' -> 'http://localhost:12345/preview/fred?full=1'
function transformUri(uri: vscode.Uri): vscode.Uri;
class StorybookContentProvider implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string {
var httpUri = transformUri(uri);
return `<iframe src="${httpUri}" />`;
}
onDidChange = new vscode.EventEmitter<vscode.Uri>();
update(uri: vscode.Uri) {
this.onDidChange(uri);
}
}
export function activate(context: vscode.ExtensionContext)
{
vscode.workspace.onDidChangeTextDocument(
(e: vscode.TextDocumentChangeEvent) => {
if (e.document === vscode.window.activeTextEditor.document) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
vscode.window.onDidChangeTextEditorSelection(
(e: vscode.TextEditorSelectionChangeEvent) => {
if (e.textEditor === vscode.window.activeTextEditor) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
const provider = new StorybookContentProvider();
context.subscriptions.push(
vscode.commands.registerCommand('extension.showStorybook', () => {
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.parse('storybook://preview'), 2, 'Storybook')
}),
vscode.workspace.registerTextDocumentContentProvider('storybook', provider)
);
}