Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reducer not updating component... #55

Closed
hydrotik opened this issue Mar 1, 2016 · 4 comments
Closed

reducer not updating component... #55

hydrotik opened this issue Mar 1, 2016 · 4 comments
Assignees
Labels

Comments

@hydrotik
Copy link
Owner

hydrotik commented Mar 1, 2016

Pretty sure I'm not mutating it, unless you consider setting default state props? meh

reduxjs/redux#153
reduxjs/redux#585

@hydrotik hydrotik added the bug label Mar 1, 2016
@hydrotik hydrotik self-assigned this Mar 1, 2016
@hydrotik
Copy link
Owner Author

hydrotik commented Mar 1, 2016

/// <reference path="../../../../../../typings/tsd.d.ts" />

import * as lodash from 'lodash';

import {
    SEND_REQUEST,
    RECEIVE_RESPONSE,
    IFormMapping
} from '../actions';

const initState: any = {
    success: false,
    hasError: {
        name: ''
    },
    help: {
        name: ''
    },
    loading: false,
    errormessage: ''
};

export function formSignup(state: any = initState, action: IFormMapping): any {
    let delta: Object;
    switch (action.type) {
        case SEND_REQUEST:
            delta = lodash.assign({}, state, {
                name: action.name,
                username: action.username,
                password: action.password,
                email: action.email
            });
            return delta;
        case RECEIVE_RESPONSE:
            delta = lodash.assign({}, state, {
                success: action.success,
                errormessage: action.errormessage,
                hasError: action.hasError,
                help: action.help,
                loading: action.loading
            });
            return delta;
        default:
            return state;
    }
}

@hydrotik
Copy link
Owner Author

hydrotik commented Mar 1, 2016

/// <reference path="../../../../../../typings/tsd.d.ts" />

import Fetch from '../../../api/jsonfetch';
import ParseValidation, { IValidation } from '../../../api/parsevalidation';
// import fetch from 'isomorphic-fetch';

export const FORM_INIT: string = 'FORM_INIT';
export const SEND_REQUEST: string = 'SEND_REQUEST';
export const RECEIVE_RESPONSE: string = 'RECEIVE_RESPONSE';

/* **************** Form Send Action Interface ****************** */
// start(callback: (startStatus: bool, engineType: string) => void) : void;
// http://stackoverflow.com/questions/17865620/typescript-multiple-inheritance-workarounds
export interface IFormAbstract {
    type: string;
}

export interface IFormRequest extends IFormAbstract {
    name: string;
    username: string;
    password: string;
    email: string;
}

export interface IFormResponse extends IFormAbstract {
    success: boolean;
    errormessage: string;
    hasError: any;
    help: any;
    loading: boolean;
}

export interface IFormMapping extends IFormRequest, IFormResponse {
    // success?: boolean;
    // error?: boolean;
    // hasError?: any;
    // help?: any;
    // loading?: boolean;
    // name?: string;
    // username?: string;
    // password?: string;
    // email?: string;
}

/* **************** Form Send Action Event ********************** */
export function onFormInit(): IFormAbstract {
    return {
        type: FORM_INIT
    };
}

/* **************** Form Send Action Event ********************** */
export function onSendFormAction(name: string, username: string, password: string, email: string): IFormRequest {
    return {
        type: SEND_REQUEST,
        name,
        username,
        password,
        email
    };
}

/* **************** Form Receive Action Event ********************** */
export function onReceiveFormAction(success: boolean, errormessage: string, hasError: any, help: any, loading: boolean): IFormResponse {
    return {
        type: RECEIVE_RESPONSE,
        success: success,
        errormessage: errormessage,
        hasError: hasError,
        help: help,
        loading: loading
    };
}

// TODO Interface for Data
export function handleRequest(data: any): any {
    return (dispatch: any, getState: any) => {
        dispatch(onSendFormAction(data.name, data.username, data.password, data.email));

        let request: any = {
            method: 'POST',
            url: '/api/signup',
            data: data
        };

        Fetch(request, (err: any, response: any) => {
            if (!err) {
                window.location.href = '/account';
                response.success = true;
            }

            let validation: IValidation = ParseValidation(response.validation, response.message);

            dispatch(onReceiveFormAction(response.success, validation.error, validation.hasError, validation.help, response.loading));
        });
    };
}

@hydrotik
Copy link
Owner Author

hydrotik commented Mar 1, 2016

/// <reference path='../../../../../../../typings/tsd.d.ts' />

// Core Imports
import * as React from 'react';
import { connect } from 'react-redux';

// Styles
import './_Form.scss';

// Page Components
import { ControlGroup } from './components/ControlGroup/ControlGroup';
import { TextControl } from './components/TextControl/TextControl';
import { Button } from './components/Button/Button';
import { Spinner } from './components/Spinner/Spinner';

// Behaviors and Actions
import {
    // SEND_REQUEST,
    // RECEIVE_RESPONSE,
    IFormMapping,
    // onFormAction,
    // handleRequest,
    onFormInit,
    onReceiveFormAction
} from '../../actions';

// Interfaces
interface IFormProps {
    dispatch?: (func: any) => void;
    store?: any;
}
interface IFormState {
    success?: boolean;
    errormessage?: string;
    hasError?: any;
    help?: any;
    loading?: boolean;

    name?: string;
    username?: string;
    password?: string;
    email?: string;
}

// Decorators
function select(state: { formSignup: IFormMapping; }): IFormState {
    const { formSignup }: { formSignup: IFormMapping; } = state;
    const {
        errormessage,
        name,
        username,
        password,
        email,
        success,
        hasError,
        help,
        loading
    }: IFormMapping = formSignup;

    console.warn('select() :: errormessage');
    console.warn(errormessage);

    return {
        errormessage,
        name,
        username,
        password,
        email,
        success,
        hasError,
        help,
        loading
    };

}

@connect(select)
export class Form extends React.Component<IFormProps, IFormState> {

    public constructor(props: any = {}) {
        super(props);
        console.warn('constructor()');
        this.state = {
            success: false,
            hasError: {
                name: ''
            },
            help: {
                name: ''
            },
            loading: false,
            errormessage: 'init'
        };
    }

    public componentDidMount(): void {
        // this.refs.nameControl.refs.inputField.getDOMNode().focus();
        console.warn('componentDidMount()');
        const { dispatch }: IFormProps = this.props;
        dispatch(
            onFormInit()
        );
    }

    public onSubmit(event: any): void {

        event.preventDefault();
        event.stopPropagation();

        const { dispatch }: IFormProps = this.props;

        /*
        dispatch(
            handleRequest({
                name: this.state.name,
                username: this.state.username,
                password: this.state.password,
                email: this.state.email
            })
        );
        */

        dispatch(
            onReceiveFormAction(false, 'ERROR', {}, {}, false)
        );
    }

    public linkState(value: string): any {
        return {
            value: this.state[value],
            requestChange: function(newValue: string): void {
                this.state[value] = newValue;
            }
        };
    }

    public render(): React.ReactElement<{}> {

        let alerts: any[] = [];
        console.warn('render()');
        console.warn('this.state.errormessage');
        console.warn(this.state.errormessage);

        if (this.state.success) {
            alerts.push(<div key='success' className='alert alert-success'>
                Success.Redirecting...
            </div>);
        }

        if (this.state.errormessage !== '') {
            alerts.push(<div key='danger' className='alert alert-danger'>
                {this.state.errormessage}
            </div>);
        }

        let formElements: React.ReactElement<{}>;
        if (!this.state.success) {
            formElements = <fieldset>
                <TextControl
                    name='name'
                    label='Name'
                    ref='nameControl'
                    hasError={this.state.hasError.name}
                    valueLink={this.linkState('name') }
                    help={this.state.help.name}
                    disabled={this.state.loading}
                    />
                <TextControl
                    name='email'
                    label='Email'
                    hasError={this.state.hasError.email}
                    valueLink={this.linkState('email') }
                    help={this.state.help.email}
                    disabled={this.state.loading}
                    />
                <TextControl
                    name='username'
                    label='Username'
                    hasError={this.state.hasError.username}
                    valueLink={this.linkState('username') }
                    help={this.state.help.username}
                    disabled={this.state.loading}
                    />
                <TextControl
                    name='password'
                    label='Password'
                    type='password'
                    hasError={this.state.hasError.password}
                    valueLink={this.linkState('password') }
                    help={this.state.help.password}
                    disabled={this.state.loading}
                    />
                <ControlGroup hideLabel={true} hideHelp={true}>
                    <Button
                        type='submit'
                        inputClasses={{ 'btn-primary': true }}
                        disabled={this.state.loading}>

                        Create my account
                        <Spinner space='left' show={this.state.loading} />
                    </Button>
                </ControlGroup>
            </fieldset>;
        }
        return (
            <section>
                <h1 className='page-header'>Sign up</h1>
                <form onSubmit={(e: any) => this.onSubmit(e) }>
                    {alerts}
                    {formElements}
                </form>
            </section>
        );
    }
}

@hydrotik
Copy link
Owner Author

hydrotik commented Mar 1, 2016

Turns out it's true, state defaults come from reducer defaults in arguments.

/// <reference path="../../../../../../typings/tsd.d.ts" />

import * as lodash from 'lodash';

import {
    FORM_UPDATE,
    SEND_REQUEST,
    RECEIVE_RESPONSE,
    IFormMapping
} from '../actions';

export function formSignup(
    state: any = {
        name: '',
        username: '',
        password: '',
        email: '',
        success: false,
        hasError: {
        },
        help: {
        },
        loading: false,
        errormessage: '',
        field: '',
        value: ''
    },
    action: IFormMapping
): any {
    let delta: Object;
    switch (action.type) {
        case FORM_UPDATE:
            let obj: any = {};
            obj[action.field] = action.value;
            delta = lodash.assign({}, state, obj);
            return delta;
        case SEND_REQUEST:
            delta = lodash.assign({}, state, {
                name: action.name,
                username: action.username,
                password: action.password,
                email: action.email
            });
            return delta;
        case RECEIVE_RESPONSE:
            delta = lodash.assign({}, state, {
                success: action.success,
                errormessage: action.errormessage,
                hasError: action.hasError,
                help: action.help,
                loading: action.loading
            });
            return delta;
        default:
            return state;
    }
}

And don't use state, that's the whole point of the connect() decorator is to map that to props.

/// <reference path='../../../../../../../typings/tsd.d.ts' />

// Core Imports
import * as React from 'react';
import { connect } from 'react-redux';

// Styles
import './_Form.scss';

// Page Components
import { ControlGroup } from './components/ControlGroup/ControlGroup';
import { TextControl } from './components/TextControl/TextControl';
import { Button } from './components/Button/Button';
import { Spinner } from './components/Spinner/Spinner';

// Behaviors and Actions
import {
    // SEND_REQUEST,
    // RECEIVE_RESPONSE,
    IFormMapping,
    // onFormAction,
    onFormUpdate,
    handleRequest,
    onFormInit,
    // onReceiveFormAction
} from '../../actions';

// Interfaces
interface IFormProps {
    dispatch?: (func: any) => void;
    store?: any;

    name?: string;
    username?: string;
    password?: string;
    email?: string;
}
interface IFormState {
    success?: boolean;
    errormessage?: string;
    hasError?: any;
    help?: any;
    loading?: boolean;
    name?: string;
    username?: string;
    password?: string;
    email?: string;
    field?: string;
    value?: string;
}

// Decorators
function select(state: { formSignup: IFormMapping; }): IFormState {
    const { formSignup }: { formSignup: IFormMapping; } = state;
    const {
        errormessage,
        name,
        username,
        password,
        email,
        success,
        hasError,
        help,
        loading,
        field,
        value
    }: IFormMapping = formSignup;

    return {
        errormessage,
        name,
        username,
        password,
        email,
        success,
        hasError,
        help,
        loading,
        field,
        value
    };

}

@connect(select)
export class Form extends React.Component<IFormProps, IFormState> {

    public constructor(props: any = {}) {
        super(props);
    }

    public componentDidMount(): void {
        // this.refs.nameControl.refs.inputField.getDOMNode().focus();
        const { dispatch }: IFormProps = this.props;
        dispatch(
            onFormInit()
        );
    }

    public handleChange(event: any): void {
        const { dispatch }: IFormProps = this.props;
        dispatch(
            onFormUpdate(event.target.name, event.target.value)
        );
    }

    public onSubmit(event: any): void {

        event.preventDefault();
        event.stopPropagation();

        const {
            dispatch,
            name,
            username,
            password,
            email
        }: IFormProps = this.props;

        dispatch(
            handleRequest({
                name,
                username,
                password,
                email
            })
        );
    }

    public render(): React.ReactElement<{}> {

        let alerts: any[] = [];

        const {
            errormessage,
            name,
            username,
            password,
            email,
            success,
            hasError,
            help,
            loading
        }: IFormState = this.props;

        if (success) {
            alerts.push(<div key='success' className='alert alert-success'>
                Success.Redirecting...
            </div>);
        }

        if (errormessage !== '') {
            alerts.push(<div key='danger' className='alert alert-danger'>
                {errormessage}
                </div>);
        }

        let formElements: React.ReactElement<{}>;
        if (!success) {
            formElements = <fieldset>
                <TextControl
                    name='name'
                    label='Name'
                    ref='nameControl'
                    hasError={hasError.name}
                    value={name}
                    onChange={ (e: any) => this.handleChange(e) }
                    help={help.name}
                    disabled={loading}
                    />
                <TextControl
                    name='email'
                    label='Email'
                    hasError={hasError.email}
                    value={email}
                    onChange={ (e: any) => this.handleChange(e) }
                    help={help.email}
                    disabled={loading}
                    />
                <TextControl
                    name='username'
                    label='Username'
                    hasError={hasError.username}
                    value={username}
                    onChange={ (e: any) => this.handleChange(e) }
                    help={help.username}
                    disabled={loading}
                    />
                <TextControl
                    name='password'
                    label='Password'
                    type='password'
                    hasError={hasError.password}
                    value={password}
                    onChange={ (e: any) => this.handleChange(e) }
                    help={help.password}
                    disabled={loading}
                    />
                <ControlGroup hideLabel={true} hideHelp={true}>
                    <Button
                        type='submit'
                        inputClasses={{ 'btn-primary': true }}
                        disabled={loading}>

                        Create my account
                        <Spinner space='left' show={loading} />
                    </Button>
                </ControlGroup>
            </fieldset>;
        }
        return (
            <section>
                <h1 className='page-header'>Sign up</h1>
                <form onSubmit={(e: any) => this.onSubmit(e) }>
                    {alerts}
                    {formElements}
                </form>
            </section>
        );
    }
}

@hydrotik hydrotik closed this as completed Mar 1, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant