Redux Toolkit linting 悖论

Posted

技术标签:

【中文标题】Redux Toolkit linting 悖论【英文标题】:Redux Toolkit linting paradox 【发布时间】:2021-08-07 04:44:23 【问题描述】:

我在文件searchSlice.js 中定义了一个 Redux Toolkit 切片,它负责查询 API 并将响应数据存储在 store 的 state 中。目前看起来是这样的:

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;
    
  
);

正如我所见,除了导出实际的切片对象本身之外,您还必须导出其必要和附带的组件:

操作 减速机 选择器 谢谢

由于上述组件高度耦合和相互依赖的性质,ESLint 将根据 searchSlice.js 文件中组件的顺序(按行号)抛出不同的 linting 错误。比如上面的代码sn-p,linting的错误是:

'getMovies' was used before it was defined. eslint(no-use-before-define)

如果我们尝试通过将 getMovies 函数声明重新排列到调用上方来修复错误,如下所示:

// ...

// 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;
      );
  ,
);

// ...

然后我们得到相同的 linting 错误,但函数定义不同:

'updateQuery' was used before it was defined. eslint(no-use-before-define)

似乎不管文件的排列方式如何,ESLint 都会抛出一个自相矛盾的no-use-before-define 错误。

是否有不涉及更改 ESLint 规则的解决方案?有没有更好的方法来构建代码?我已经尝试将其拆分为更小的文件,但由于随附切片功能的高度相互依赖性质,ESLint 将开始抛出 import/no-cycle 错误,因为两个文件都需要相互导入内容。

作为一个附加问题,提升在这里如何发挥作用?

【问题讨论】:

【参考方案1】:

当你处理一个循环时,你需要弄清楚哪个是有意义的依赖,哪个是依赖。在这种情况下,从 thunk 中删除 reducer 比从 reducer 中删除 thunk 更容易。

您可以通过从 thunk 中删除额外的已调度 updateQuery 操作并在您的 reducer 中处理该逻辑来修复依赖关系。您可以通过action.meta.arg 属性从getMovies.pending case reducer 中的thunk 访问(名称混淆)payload 变量,该属性包含您调用thunk 操作创建者时使用的参数。

export const getMovies = createAsyncThunk(
  'search/getMovies',
  async (query) => 
    const res = await axios.get(`/search?query=$query`);
    return res.data;
    // Don't catch errors here. Let them be thrown and handled by the 'rejected' action.
  
);

export const searchSlice = createSlice(
  name: 'search',
  initialState,
  reducers: 
    // you might not even need this anymore, unless you use it elsewhere.
    updateQuery: (state, action) => 
      state.query = action.payload;
    ,
  ,
  extraReducers: (builder) => 
    builder
      .addCase(getMovies.pending, (state, action) => 
        // Update the query property of the state.
        state.query = action.meta.arg;
        state.status = 'pending';
      )
      .addCase(getMovies.fulfilled, (state, action) => 
        state.status = 'idle';
        state.movies = action.payload.results;
        state.totalResults = action.payload.total_results;
      );
  ,
);

顺便说一句,条件if (!store.getState().search) 没有意义。那将检查整个切片是否真实,它总是如此。

【讨论】:

以上是关于Redux Toolkit linting 悖论的主要内容,如果未能解决你的问题,请参考以下文章

如何实现redux-toolkit和next,js又不丢s-s-r

从 React Native Text Input 向 Redux Toolkit 的 createApi 添加动态数据?

Redux-Toolkit createAsyncThunk with Typescript

Reducer 状态没有被新对象更新 [redux, redux-toolkit, normalize]

Redux Toolkit

react——Redux Toolkit