简体   繁体   中英

Redux Toolkit createSlice in React

I'm learning Redux-Saga and everything works well with { configureStore, getDefaultMiddleware, createAction, createReducer } . However, I cannot successfully implement createSlice .

My actions seem to be dispatched just fine (though I'm not sure since I have multiple Redux stores and placing console.log inside createSlice doesn't seem to work...). I just cannot get the store values - after dispatched action the relevant state value (initially '' ) becomes undefined . I did wrap my component inside Provider and all. Can someone enlighten me how does createSlice work? Thanks.

RESOLVED I had a bug somewhere else in my code, that's why the reducers weren't working proberly. BUT what I was asking about and what was causing my problems is this: actions passed to createSlice must be 'pure' functions, meaning: (state, action) -> state , nothing fancy. That's why I had to remove my fetching functions ( getData1 and getData2 ) from this createSlice .

ComponentWrapper returns this

<Provider store={toolkitCreateSliceStore}>
    <ReduxToolkitCreateSliceComponent />
</Provider>

Component (Buttons just dispatch actions)

class ReduxToolkitCreateSliceComponent extends React.Component {

    render () {
        return (
            <>
                <h2>
                    {this.props.data1}
                    {(this.props.data1!=='' && this.props.data2!=='') ? ', ' : ''}
                    {this.props.data2}
                </h2><br/>
                <h3>{this.props.message}</h3>
                <Button1 />
                <Button2 />
                <Button3 />
            </>
        );
    }
}

function mapStateToProps(state) {
    return {
        data1: state.toolkitCreateSliceReducer.data1,
        data2: state.toolkitCreateSliceReducer.data2,
        message: state.toolkitCreateSliceReducer.message
    };
}

export default connect(mapStateToProps)(ReduxToolkitCreateSliceComponent);

Redux Toolkit slice

import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";

const initialSliceState = {
    data1: '',
    data2: '',
    message: ''
};

const slice = createSlice({
    name: "slice",
    initialState: initialSliceState,
    reducers: {
        getData1: (state, action) => {
            return dispatch => {
                dispatch(loading1());
                return axios.get('http://localhost:8081/data1')
                    .then(function (response) {
                        if (response.status === 200) {
                            dispatch(setResponse1(response.data));
                        }
                    }).catch(error => dispatch(displayError1(error)));
            };
        },
        getData2: (state, action) => {
            return dispatch => {
                dispatch(loading2());
                return axios.get('http://localhost:8081/data2')
                    .then(function (response) {
                        if (response.status === 200) {
                            dispatch(setResponse2(response.data));
                        }
                    }).catch(error => dispatch(displayError2(error)));
            };
        },
        setResponse1: (state, action) => {
            state.data1 = action.payload;
            state.message = 'success';
        },
        setResponse2: (state, action) => {
            state.data2 = action.payload;
            state.message = 'success';
        },
        reset: (state, action) => {
            state.data1 = '';
            state.data2 = '';
            state.message = 'reset';
        },
        loading1: (state, action) => {
            state.message = 'loading';
        },
        loading2: (state, action) => {
            state.message = 'loading';
        },
        displayError1: (state, action) => {
            state.message = action.payload;;
        },
        displayError2: (state, action) => {
            state.message = action.payload;;
        }
    }
});

export const toolkitCreateSliceReducer = slice.reducer;

const { getData1, getData2, setResponse1, setResponse2, reset, loading1, loading2,
    displayError1, displayError2} = slice.actions;

export default slice;

Redux Toolkit store

const middleware = [
    ...getDefaultMiddleware()
];

const toolkitCreateSliceStore = configureStore({
    reducer: {
        toolkitCreateSliceReducer
    },
    middleware
});

export default toolkitCreateSliceStore;

Your "reducers" are very wrong.

A reducer must never have any side effects like AJAX calls .

You've written some Redux "thunk" functions where your reducers should be:

getData1: (state, action) => {
            return dispatch => {
                dispatch(loading1());
                return axios.get('http://localhost:8081/data1')
                    .then(function (response) {
                        if (response.status === 200) {
                            dispatch(setResponse1(response.data));
                        }
                    }).catch(error => dispatch(displayError1(error)));
            };
        },

This is a thunk, not a reducer.

A reducer would be something like:

getData(state, action) {
  return action.payload;
}

I'd specifically recommend reading through our brand-new "Redux Essentials" core docs tutorial, which teaches beginners "how to use Redux, the right way", using our latest recommended tools and practices like Redux Toolkit. It specifically covers how reducers should work, how to write reducers with createSlice , and how to write and use thunks alongside createSlice :

https://redux.js.org/tutorials/essentials/part-1-overview-concepts

If you're interested in creating async actions, let me recommend you an npm package that I created and use. It is saga-toolkit that allows async functions to get resolved by sagas.

slice.js

import { createSlice } from '@reduxjs/toolkit'
import { createSagaAction  } from 'saga-toolkit'

const name = 'example'

const initialState = {
  result: null,
  loading: false,
  error: null,
}

export const fetchThings = createSagaAction(`${name}/fetchThings`)

const slice = createSlice({
  name,
  initialState,
  extraReducers: {
    [fetchThings.pending]: () => ({
      loading: true,
    }),
    [fetchThings.fulfilled]: ({ payload }) => ({
      result: payload,
      loading: false,
    }),
    [fetchThings.rejected]: ({ error }) => ({
      error,
      loading: false,
    }),
  },
})

export default slice.reducer

sagas.js

import { call } from 'redux-saga/effects'
import { takeLatestAsync } from 'saga-toolkit'
import API from 'hyper-super-api'
import * as actions from './slice'

function* fetchThings() {
  const result = yield call(() => API.get('/things'))
  return result
}

export default [
  takeLatestAsync(actions.fetchThings.type, fetchThings),
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM