[PL] Let’s Write – ahh te formularze….
Tempo pracy z Reactem wygląda u mnie jak sinusoida. Najpierw bardzo powoli, potem coraz szybciej i znowu kodu przestaje przybywać. Pomijając ilość czasu jaką mam to taki stan rzeczy wynika z trudności jakie spotykam. Na początku wiadomo, trzeba się wdrożyć w bibliotekę. Potem poznało się podstawy więc kolejne elementy idą do przodu. Po czym trafia się na następne przeszkody, które zwalniają pracę. Po ich rozwiązaniu na pewno przyjdzie czas przyśpieszenia.
Pierwsze dwa etapy przeszedłem. Czas na trzeci.
Poprzednie wpisy z serii:
Formularz logowania
W poprzednim wpisie wspomniałem, że zabieram się za formularze i dodawanie handlerów dla inputów. Na początek wziąłem się za formularz logowania bo po prostu ma mało pól więc szybciej idzie.
Dodałem handlery dla poszczególnych inputów. Wiem, że całym zarządzaniem stanem aplikacji zajmuje się Redux, ale uznałem, że wartości pól są lokalne i chwilowe dlatego dodałem też stan w komponencie. Kontener na formularz logowania wygląda teraz tak:
const LoginContainer = React.createClass({ getInitialState () { return { username: "", password: "", remember: false } }, render: function() { return ( <Login formState={this.state} usernameHandler={this.usernameChangeHandler} passwordHandler={this.passwordChangeHandler} rememberMeHandler={this.rememberMeChangeHandler} loginUser={this.loginUser} /> ) }, loginUser: function() { store.dispatch(commonActions.blockUi()) var data = this.state; setTimeout(() => { store.dispatch(userActions.loginUserSuccess({ firstName: 'Marek', lastName: 'Zając' })) store.dispatch(commonActions.unblockUi()) browserHistory.push('/') }, 2000) }, //changeHandlers usernameChangeHandler: function(event) { this.setState({username: event.target.value}) }, passwordChangeHandler: function(event) { this.setState({password: event.target.value}) }, rememberMeChangeHandler: function(event) { this.setState({remember: event.target.checked}) } })
Nie wiem czy dobrze robię przekazując stan w ten sposób, tzn. przekazując całą zmienną state. Może powinienem to zrobić inaczej? Napiszcie w komentarzach :)
Podobnie mam wątpliwość co do handlerów. W formularzu logowania przekazuję je pojedynczo. Później zobaczycie, że przy rejestracji zrobiłem to trochę inaczej – dodałem obiekt ze wszystkimi handlerami przez co mam mniej parametrów dla widoku.
Ta dziwna konstrukcja w metodzie loginUser() służy do testowania blokowania ekranu przy wysyłaniu requestu. Nie mam jeszcze serwera więc muszę jakoś zasymulować opóźnienie przy przetwarzaniu danych :D
Widoki i kod odpowiedzialny za logikę podzieliłem na osobne komponenty bo tak powiedzieli na stronie https://css-tricks.com/learning-react-container-components/. Wydaje się to sensowne. Chyba nawet w dokumentacji Reacta jest o tym powiedziane więc będę tak robił nadal.
Sam komponent Login wzbogacił się tylko o wykorzystanie parametrów dla wartości inputów i zdarzeń onChange:
const Login = React.createClass({ render: function() { return ( <Row> <Col md={4}> </Col> <Col md={4}> <Form horizontal> <FormGroup controlId='email'> <Col componentClass={ControlLabel} sm={2}> Email </Col> <Col sm={10}> <FormControl value={this.props.formState.username} onChange={this.props.usernameHandler} type='email' placeholder='Email'/> </Col> </FormGroup> <FormGroup controlId='password'> <Col componentClass={ControlLabel} sm={2}> Password </Col> <Col sm={10}> <FormControl value={this.props.formState.password} onChange={this.props.passwordHandler} type='password' placeholder='Password'/> </Col> </FormGroup> <FormGroup controlId='remember-me'> <Col smOffset={2} sm={10}> <Checkbox onChange={this.props.rememberMeHandler} checked={this.props.formState.remember}>Remember me</Checkbox> </Col> </FormGroup> <FormGroup> <Col smOffset={2} sm={10}> <Button onClick={this.props.loginUser}> Sign in </Button> </Col> </FormGroup> </Form> </Col> </Row> ) } })
Robiąc te formularze widzę, że będę musiał chyba podzielić je na mniejsze komponenty.
Nie pokazuję zrzutów ekranu bo wygląd się nie zmienił.
Formularz rejestracji
Przy formularzu rejestracji już nie szło tak prosto bo było tutaj więcej pól. Całość wygląda podobnie. Największa zmiana jest taka, że handlery zamknąłem w jednej zmiennej, którą przekazuję do widoku. Po prostu było ich za dużo, żeby każdy podawać osobno.
const RegisterContainer = React.createClass({ getInitialState() { return { firstName: "", lastName: "", email: "", password: "", repeatPassword: "", acceptTAC: false } }, render: function() { return ( <Register formState={this.state} registerUser={this.registerUser} handlers={this.inputHandlers} ></Register> ) }, registerUser() { if(!this.state.firstName) { } return true; }, inputHandlers: { firstNameChangeHandler: function(event) { this.setState({firstName: event.target.value}) }, lastNameChangeHandler: function(event) { this.setState({lastName: event.target.value}) }, emailChangeHandler: function(event){ this.setState({email: event.target.value}) }, passwordChangeHandler: function(event) { this.setState({password: event.target.value}) }, repeatPasswordChangeHandler: function(event) { this.setState({repeatPassword: event.target.value}) }, acceptTACChangeHandler: function(event) { this.setState({acceptTAC: event.target.checked}) } } })
Kolejna przeszkoda
W trakcie dodawania handlerów dla formularza rejestracji przypomniało mi się, że muszę dodać jeszcze jedną dosyć istotną rzecz – walidację. Fajnie by było sprawdzać czy np. użytkownik w pole email wpisał wartość, która jest adresem email, albo czy wypełnił wszystkie wymagane pola :D
To jest ten moment kiedy znowu zwolniłem. Muszę poszukać jak najlepiej zrobić walidację pól używając Reacta i Bootstrapa.
W takich momentach człowiek z jednej strony jest zły, że praca stoi, ale z drugiej strony próbuje sobie powtarzać, że jak teraz rozwiąże problem to później to rozwiązanie będzie używał wszędzie.
Dlatego na dzisiaj tyle. Mam nadzieję, że za kilka dni walidacja będzie działała.
Pisanie formularzy jest mało kreatywną pracą to fakt. Mam taką uwagę, jeśli robisz login i ustawiasz sobie timeout, to dobrą praktyką jest przypisanie tego timeoutu do zmiennej, a następnie wyczyścić timeout za pomocą clearTimeout przy następnym wywołaniu akcji. Bo z tego co widzę, to możesz mieć wiele opóźnionych akcji wywołanych co może spowodować nieoczekiwane reakcje interfejsu.
Słuszna uwaga. Jednak ten timeout jest tylko tymczasowo. Po prostu chciałem sprawdzić działanie blokowania widoku przy wysyłaniu formularza i prawdopodobnie wyleci to przy najbliższej okazji.
Ale zapiszę sobie, żeby pamiętać o czyszczeniu timeoutów w przyszłości.