2016-04-13 4 views
6

Co potrzebne: A controlled component że jest input[type="search"]. Po powiedzeniu, 1 sekunda braku zmian w tym komponencie chcę wysłać połączenie HTTP, aby wykonać to wyszukiwanie i wyświetlić wyniki w innym komponencie.poprawny sposób udusić połączeń HTTP na podstawie stanu w Redux i reagować

Jak robiłem go: Ilekroć zadzwonić dispatch(setSearch(str)); nawiązywać połączenia do dispatch(displaySearch(false)); a następnie _.throttle wezwanie do dispatch(displaySearch(true));

on czuje do mnie, że robi tego rodzaju pracy w komponencie jest nieprawidłowy , ale nie mogę wymyślić sposób, aby to zrobić w reduktorze w redux.

+0

Chcesz cos globalny dla wszystkich aplikacji (jak dla wszystkich wywołań API), czy tylko dla tego składnika? – KeitIG

+0

Widzę, że jest używany w innych komponentach w tej sekcji aplikacji. – Dave

+0

patrz http://stackoverflow.com/questions/23123138/perform-debounce-in-react-js/28046731#28046731 –

Odpowiedz

4

Masz różne możliwości rozwiązania tego problemu.

1. odbicia twoje działanie na poziomie komponentu

Jest to najprostszy podejście. Kiedy wejście wyzwala zmianę, wywołuje zdeklarowaną wersję setSearch opóźniającą wywołanie serwera.

import * as React from "react" 
import {connect} from "react-redux" 
import {setSearch} from "./actions" 

export default connect(
    null, 
    function mapDispatchToProps(dispatch) { 
    const setSearch_ = _.debounce(q => dispatch(setSearch(q)), 1000) 
    return() => ({setSearch: setSearch_}) 
    } 
)(
    function SearchForm(props) { 
    const {setSearch} = props 
    return (
     <input type="search" onChange={setSearch} /> 
    ) 
    } 
) 

2. odbicia za pomocą redux-saga

Podejście to wymaga więcej boilerplate ale daje dużo większą kontrolę nad workflow. Korzystając z saga przechwycimy akcję , odbijamy ją, wywołujemy interfejs API, a następnie wywołujemy nową akcję zawierającą wyniki.

import {call, cancel, fork, put, take} from "redux-saga/effects" 
import {setSearchResults} from "./actions" 
import {api} from "./services" 
import {delay} from "./utils" 

export default function* searchSaga() { 
    yield [ 
    // Start a watcher to handle search workflow 
    fork(watchSearch) 
    ] 
} 

function* watchSearch() { 
    let task 

    // Start a worker listening for `SET_SEARCH` actions. 
    while (true) { 
    // Read the query from the action 
    const {q} = yield take("SET_SEARCH") 

    // If there is any pending search task then cancel it 
    if (task) { 
     yield cancel(task) 
    } 

    // Create a worker to proceed search 
    task = yield fork(handleSearch, q) 
    } 
} 

function* handleSearch(q) { 
    // Debounce by 1s. This will lock the process for one second before 
    // performing its logic. Since the process is blocked, it can be cancelled 
    // by `watchSearch` if there are any other actions. 
    yield call(delay, 1000) 

    // This is basically `api.doSearch(q)`. The call should return a `Promise` 
    // that will resolve the server response. 
    const results = yield call(api.doSearch, q) 

    // Dispatch an action to notify the UI 
    yield put(setSearchResults(results)) 
} 
+1

Zły pomysł: tworzenie funkcji wyrzuconej na 'mapDispatchToProps' oznacza, że ​​funkcja wyrzucona zostanie odtworzona za każdym razem gdy stan zmiany, co oznacza, że ​​nie zostaną one faktycznie odrzucone. Również dla sagi redux istnieje dokument na ten temat: http://yelouafi.github.io/redux-saga/docs/recipes/index.html –

+0

@SebastienLorber Miałeś rację odnośnie 'mapDispatchToProps'. Zaktualizowałem swoją odpowiedź, to już nie jest sprawa, dzięki! I tak, mój przykładowy kod został zapożyczony z receptur sagi redux. – Florent

1

Associate delay i takeLatest:

import { all, takeLatest, call } from 'redux-saga/effects'; 
import { delay } from 'redux-saga'; 


function* onSearch(action) { 
    yield call(delay, 1000); // blocks until a new action is 
          // triggered (takeLatest) or until 
          // the delay of 1s is over 

    const results = yield call(myFunction); 
} 

function* searchSagas() { 
    yield all([ 
    // ... 
    takeLatest(ON_SEARCH, onSearch), 
    ]); 
} 

export default [searchSagas]; 
Powiązane problemy