I have defined a Redux Toolkit slice within a file, searchSlice.js, which is responsible for querying an API and storing the response data in the store's state. It currently looks like this:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
const initialState = {
query: '',
status: 'idle',
movies: [],
totalResults: null,
};
// Create slice
export const searchSlice = createSlice({
name: 'search',
initialState,
reducers: {
updateQuery: (state, action) => {
state.query = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(getMovies.pending, (state) => {
state.status = 'pending';
})
.addCase(getMovies.fulfilled, (state, action) => {
state.status = 'idle';
state.movies = action.payload.results;
state.totalResults = action.payload.total_results;
});
},
});
// Actions
export const { updateQuery } = searchSlice.actions;
// Reducers
export default searchSlice.reducer;
// Selectors
export const selectQuery = (state) => state.search.query;
export const selectStatus = (state) => state.search.status;
export const selectAllMovies = (state) => state.search.movies;
export const selectTotalResults = (state) => state.search.totalResults;
// Thunks
export const getMovies = createAsyncThunk(
'search/getMovies',
async (payload, store) => {
if (!store.getState().search) {
dispatchEvent(updateQuery(payload));
}
try {
console.log('payload: ', payload);
const res = await axios.get(`/search?query=${store.getState().search}`);
return res.data;
} catch (err) {
return err;
}
}
);
As I see it, besides exporting the actual slice object itself, you also must export its necessary and accompanying components:
- Actions
- Reducers
- Selectors
- Thunks
Due to the nature of the aforementioned components being highly coupled and interdependent, ESLint will throw different linting errors based on the order of the components within the searchSlice.js file, by line number. For example, in the above code snippet, the linting error is:
'getMovies' was used before it was defined. eslint(no-use-before-define)
And if we attempt to fix the error by rearranging the getMovies function declaration to above the invocation, like so:
// ...
// Thunks
export const getMovies = createAsyncThunk(
'search/getMovies',
async (payload, store) => {
if (!store.getState().search) {
dispatchEvent(updateQuery(payload));
}
try {
console.log('payload: ', payload);
const res = await axios.get(`/search?query=${store.getState().search}`);
return res.data;
} catch (err) {
return err;
}
}
);
// ...
// Create slice
export const searchSlice = createSlice({
name: 'search',
initialState,
reducers: {
updateQuery: (state, action) => {
state.query = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(getMovies.pending, (state) => {
state.status = 'pending';
})
.addCase(getMovies.fulfilled, (state, action) => {
state.status = 'idle';
state.movies = action.payload.results;
state.totalResults = action.payload.total_results;
});
},
});
// ...
Then we get the same linting error, but with different function definitions:
'updateQuery' was used before it was defined. eslint(no-use-before-define)
It seems like regardless of the file's arrangement, ESLint will throw a paradoxical no-use-before-define error.
Is there a solution to this that doesn't involve changing the ESLint rules? Is there a better way to structure the code? I have already tried splitting it up into smaller files, but due to the highly interdependent nature of accompanying slice functionality, ESLint will start throwing import/no-cycle errors, because both files need to import stuff from eachother.
As an additional question, how does hoisting come into play here?