Svelte + Redux + Redux-saga

Upaya kemiripan yang menyedihkan dengan useSelector, hook useDispatch, seperti di react-redux.

Sebagian besar dari kita telah menemukan redux, dan mereka yang menggunakannya di ReactJS dapat merasakan hook useSelector, useDispatch, atau melalui mstp, mdtp + HOC connect. Bagaimana dengan langsing? Anda dapat mengacaukannya, atau menemukan sesuatu yang mirip untuk dihubungkan, seperti svelte-redux-connect, jelaskan konstruksi besar yang akan kami kirim ke koneksi yang sama:





const mapStateToProps = state => ({
  users: state.users,
  filters: state.filters
});

const mapDispatchToProps = dispatch => ({
  addUser: (name) => dispatch({
    type: 'ADD_USER',
    payload: { name }
  }),
  setFilter: (filter) => dispatch({
    type: 'SET_FILTER',
    payload: { filter }
  }) 
});
      
      



Hanya beberapa kilas balik yang menakutkan hingga pertengahan 2018, sebelum pengenalan kait :). Saya ingin pengait di langsing. Apa yang bisa kita ambil darinya? Hmm ... toko svelte bersifat global, tidak diperlukan penyedia dengan konteks (bercanda, mereka diperlukan untuk memisahkan konteks, tetapi mari kita buang untuk saat ini). Artinya seperti ini: kami membuat toko redux, kemudian kami akan mencoba menulis kait menyedihkan kami untuk kemudahan penggunaan.





Jadi konstanta kami:





//constants.js
export const GET_USER = '@@user/get'
export const FETCHING_USER = '@@user/fetch'
export const SET_USER = '@@user/set'
      
      



Peredam:





//user.js
import {FETCHING_USER, SET_USER} from "./constants";

const initialState = {
  user: null,
  isFetching: false
}

export default function user(state = initialState, action = {}){
  switch (action.type){
    case FETCHING_USER:
    case SET_USER:
      return {
        ...state,
        ...action.payload
      }
    default:
      return state
  }
}
      
      



Tindakan:





//actions.js
import {FETCHING_USER, GET_USER, SET_USER} from "./constants";

export const getUser = () => ({
  type: GET_USER
})

export const setUser = (user) => ({
  type: SET_USER,
  payload: {
    user
  }
})

export const setIsFetchingUser = (isFetching) => ({
  type: FETCHING_USER,
  payload: {
    isFetching
  }
})
      
      



Penyeleksi. Mari kembali ke mereka secara terpisah:





//selectors.js
import {createSelector} from "reselect";
import path from 'ramda/src/path'

export const selectUser = createSelector(
  path(['user', 'user']),
  user => user
)

export const selectIsFetchingUser = createSelector(
  path(['user', 'isFetching']),
  isFetching => isFetching
)
      
      



Dan gabungan utama





//rootReducer.js
import {combineReducers} from "redux";
import user from "./user/user";

export const reducers = combineReducers({
  user
})
      
      



Sekarang kita perlu melampirkan redux-saga, dan sebagai api kita akan memiliki https://randomuser.me/api/ . Selama pengujian seluruh proses, api ini bekerja sangat cepat, dan saya benar-benar ingin melihat loader lebih lama (setiap orang memiliki masokisme mereka sendiri), jadi saya mengubah batas waktu menjadi janji selama 3 detik.





//saga.js
import {takeLatest, put, call, cancelled} from 'redux-saga/effects'
import {GET_USER} from "./constants";
import {setIsFetchingUser, setUser} from "./actions";
import axios from "axios";

const timeout = () => new Promise(resolve => {
  setTimeout(()=>{
    resolve()
  }, 3000)
})

function* getUser(){
  const cancelToken = axios.CancelToken.source()
  try{
    yield put(setIsFetchingUser(true))
    const response = yield call(axios.get, 'https://randomuser.me/api/', {cancelToken: cancelToken.token})
    yield call(timeout)
    yield put(setUser(response.data.results[0]))
    yield put(setIsFetchingUser(false))
  }catch (error){
    console.error(error)
  }finally {
    if(yield cancelled()){
      cancelToken.cancel('cancel fetching user')
    }
    yield put(setIsFetchingUser(false))
  }
}

export default function* userSaga(){
  yield takeLatest(GET_USER, getUser)
}
      
      



//rootSaga.js
import {all} from 'redux-saga/effects'
import userSaga from "./user/saga";

export default function* rootSaga(){
  yield all([userSaga()])
}
      
      



Terakhir, menginisialisasi toko:





//store.js
import {applyMiddleware, createStore} from "redux";
import {reducers} from "./rootReducer";
import {composeWithDevTools} from 'redux-devtools-extension';
import {writable} from "svelte/store";

import createSagaMiddleware from 'redux-saga';
import rootSaga from "./rootSaga";

const sagaMiddleware = createSagaMiddleware()

const middleware = applyMiddleware(sagaMiddleware)

const store = createStore(reducers, composeWithDevTools(middleware))

sagaMiddleware.run(rootSaga)

//     store
const initialState = store.getState()

//  writable store  useSelector
export const useSelector = writable((selector)=>selector(initialState))

//  writable store  useDispatch,      
//      
export const useDispatch = writable(() => store.dispatch)

//    store
store.subscribe(()=>{
  const state = store.getState()
  //   store  useSelector,    , 
  //  ,        
  useSelector.set(selector => selector(state))
})
      
      



. 18 . , , , - useSelector 3 store - ? , , . , store , , . , , :)









, ?

c useDispatch. svelte-store

export const useDispatch = () => store.dispatch



, useSelector store bindings, useDispatch - , . useDispatch App.svelte:





<!--App.svelte-->
<script>
  import {getUser} from "./store/user/actions";
  import {useDispatch} from "./store/store";
  import Loader from "./Loader.svelte";
  import User from "./User.svelte";
  //  
  const dispatch = $useDispatch()
  const handleClick = () => {
    //  
    dispatch(getUser())
  }
</script>

<style>
    .wrapper {
        display: inline-block;
        padding: 20px;
    }
    .button {
        padding: 10px;
        margin: 20px 0;
        border: none;
        background: #1d7373;
        color: #fff;
        border-radius: 8px;
        outline: none;
        cursor: pointer;
    }
    .heading {
        line-height: 20px;
        font-size: 20px;
    }
</style>

<div class="wrapper">
    <h1 class="heading">Random user</h1>
    <button class="button" on:click={handleClick}>Fetch user</button>
    <Loader/>
    <User/>
</div>
      
      



Tombol yang memicu tindakan

. Fetch user, GET_USER. Redux-dev-tools - , . network - , :





. useSelector:





<!--Loader.svelte-->
<script>
    import {useSelector} from "./store/store";
    import {selectIsFetchingUser} from "./store/user/selector";
		//         store , 
    //       ,   :3
    $: isFetchingUser = $useSelector(selectIsFetchingUser)
</script>

<style>
    @keyframes loading {
        0% {
            background: #000;
            color: #fff;
        }
        100% {
            background: #fff;
            color: #000;
        }
    }
    .loader {
        background: #fff;
        box-shadow: 0px 0px 7px rgba(0,0,0,0.3);
        padding: 10px;
        border-radius: 8px;
        transition: color 0.3s ease-in-out, background 0.3s ease-in-out;
        animation: loading 3s ease-in-out forwards;
    }
</style>

{#if isFetchingUser}
    <div class="loader">Loading...</div>
{/if}
      
      



. store , :





<!--User.svelte-->
<script>
    import {useSelector} from "./store/store";
    import {selectIsFetchingUser,selectUser} from "./store/user/selector";

    $: user = $useSelector(selectUser)
    $: isFetchingUser = $useSelector(selectIsFetchingUser)
</script>
<style>
    .user {
        background: #fff;
        box-shadow: 0px 0px 7px rgba(0,0,0,0.3);
        display: grid;
        padding: 20px;
        justify-content: center;
        align-items: center;
        border-radius: 8px;
    }
    .user-image {
        width: 100px;
        height: 100px;
        background-position: center;
        background-size: contain;
        border-radius: 50%;
        margin-bottom: 20px;
        justify-self: center;
    }
</style>
{#if user && !isFetchingUser}
    <div class="user">
        <div class="user-image" style={`background-image: url(${user.picture.large});`}></div>
        <div>{user.name.title}. {user.name.first} {user.name.last}</div>
    </div>
{/if}
      
      



.





Kami menuliskan beberapa kesamaan dengan hook, tampaknya nyaman, tetapi tidak diketahui bagaimana pengaruhnya di masa depan, jika kami membuat mini-app dari ini untuk beberapa halaman. Hikayat juga membajak. Melalui redux devtools, Anda dapat men-debug redux dan melompat dari satu tindakan ke tindakan lain, semuanya bekerja dengan baik.








All Articles