react-reduxの場合のように、useSelector、useDispatchフックとの哀れな類似性の試み。
私たちのほとんどはreduxに出くわし、ReactJSでそれを使用した人は、useSelector、useDispatchフックを感じるかもしれません。そうでなければ、mstp、mdtp + HOC接続を介して。svelteはどうですか?あなたはそれを台無しにするか、svelte-redux-connectのような接続に似たものを見つけて、同じ接続に送信する巨大な構造を説明することができます:
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 }
})
});
フックが導入される前の2018年半ばまでの恐ろしいフラッシュバック:)。フックをスベルテにしたい。私たちはそれから何を得ることができますか?うーん... svelteのストアはグローバルであり、コンテキストを持つプロバイダーは必要ありません(冗談ですが、コンテキストを分離するために必要ですが、とりあえず捨てましょう)。つまり、redux-storeを作成してから、使いやすさのために哀れなフックを作成しようとします。
だから私たちの定数:
//constants.js
export const GET_USER = '@@user/get'
export const FETCHING_USER = '@@user/fetch'
export const SET_USER = '@@user/set'
レデューサー:
//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
}
}
行動:
//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
}
})
セレクター。別々にそれらに戻りましょう:
//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
)
そして主なcombineReducers:
//rootReducer.js
import {combineReducers} from "redux";
import user from "./user/user";
export const reducers = combineReducers({
user
})
ここで、redux-sagaをアタッチする必要があり、APIとしてhttps://randomuser.me/api/があります。プロセス全体のテスト中、このAPIは非常に迅速に機能し、ローダーをもっと長く見たかったので(誰もが独自のマゾヒズムを持っています)、タイムアウトを3秒間の約束に変えました。
//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()])
}
最後に、ストアを初期化します。
//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>
. 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}
.
フックとの類似点をいくつか書き留めました。便利なようですが、これから数ページのミニアプリを作成した場合、これが将来どのように影響するかはわかりません。サガも耕します。redux devtoolsを使用すると、reduxをデバッグして、アクションからアクションにジャンプできます。すべてが正常に機能します。