Search code examples
reactjstypescriptreduxredux-saga

Changing Redux store using input


I'm really new to React, Typescript and Redux, < a week using them.

What I need, is to store a "userName" on Redux's storage, in order to validate it against other values in the storage. My problem specifically is not being able to store the userName, mostly because all examples use class based components, while I'm using functional ones. Here's what I have as of now:

//User input component

import React, { useState } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";

import * as actions from "../../actions";

import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import { ApplicationStateInterface } from "../../combineReducers";
import { Box } from "@material-ui/core";

interface channelCreatorInterface {
    open: boolean;
    hideChannelCreator?: any;
    handleFormChange?: any;
}

const mapStateToProps = (
    state: ApplicationStateInterface,
    ownProps: channelCreatorInterface
) => {
    return {
        ...ownProps,
    };
};

const mapDispatchToProps = (dispatch: any) => ({
    hideChannelCreator() {
        dispatch(actions.hideChannelCreator());
    },
});

const ChannelCreator = (props: channelCreatorInterface) => {
    return (
        <div>
            <Dialog
                open={props.open}
                onClose={props.hideChannelCreator}
                aria-labelledby='form-dialog-title'>
                <DialogTitle id='form-dialog-title'>Create channel name</DialogTitle>
                <DialogContent>
                    <DialogContentText>
                        To create a channel name, please enter your desired name here. We
                        will check for availability.
                    </DialogContentText>
                    <TextField
                        autoFocus
                        margin='dense'
                        id='name'
                        label='Channel name'
                        type='username'
                        fullWidth
            onChange= {HERE IS THE EVENT I NEED TO CAPTURE}
                    />
                </DialogContent>
                <DialogActions>
                    <Button color='primary' onClick={props.hideChannelCreator}>
                        Cancel
                    </Button>
                    <Box id='saveButton'>
                        <Button variant='contained' color='primary'>
                            Save
                        </Button>
                    </Box>
                </DialogActions>
            </Dialog>
        </div>
    );
};
export default connect(mapStateToProps, mapDispatchToProps)(ChannelCreator);


//Form change action
import { makeActionCreator } from "../utility";
export const HANDLE_FORM_CHANGE = "HANDLE_FORM_CHANGE";
export const handleFormChange = makeActionCreator(
    HANDLE_FORM_CHANGE,
    "payload"
);


//Reducer

import { constants } from "os";
import { Reducer } from "redux";
import {
    CREATE_CHANNEL,
    DISPLAY_CHANNEL_CREATOR,
    HIDE_CHANNEL_CREATOR,
    HANDLE_FORM_CHANGE,
} from "../actions";

export interface channelCreatorInterface {
    open: boolean;
    state: "idle" | "saving" | "correctly saved";
    channelName: "";
}

export const channelCreatorInitialState: channelCreatorInterface = {
    open: false,
    state: "idle",
    channelName: "",
};

export const channelCreatorReducer: Reducer<any, any> = (
    channelCreatorState: channelCreatorInterface = channelCreatorInitialState,
    action: { type: any; state: channelCreatorInterface }
) => {
    switch (action.type) {
        case CREATE_CHANNEL:
            return {
                ...channelCreatorState,
                state: "saving",
            };
        case DISPLAY_CHANNEL_CREATOR:
            return {
                ...channelCreatorState,
                open: true,
            };
        case HIDE_CHANNEL_CREATOR:
            return {
                ...channelCreatorState,
                open: false,
            };
        case HANDLE_FORM_CHANGE:
            return {
                ...channelCreatorState,
                channelName: "",
            };
        default:
            return channelCreatorState;
    }
};



Solution

  • First, let's go to your question:

    Your reducer would need to use a payload from your action:

    
            case HANDLE_FORM_CHANGE:
                return {
                    ...channelCreatorState,
                    channelName: action.payload,
                };
    

    and in your component, you would then use an event handler to dispatch an action with that payload:

    const mapDispatchToProps = (dispatch: any) => ({
        // ...
        setChannelName: (channelName: string) => dispatch({ type: HANDLE_FORM_CHANGE, payload: channelName })
    });
    // in your component:
                        <TextField
                            autoFocus
                            margin='dense'
                            id='name'
                            label='Channel name'
                            type='username'
                            fullWidth
                onChange={event => props.setChannelName(event.target.value)}
                        />
    

    But please note, that this is a very outdated style of writing redux. For one, we recommend using the react-redux hooks useSelector and useDispatch, which can replace connect in function components. Also, we recommend using redux toolkit instead of writing plain redux reducers by hand, as that will save you a lot of code and also help a lot with the typescript typings.

    If you are looking for an up-to-date tutorial, the official Essentials Tutorial is the best place to start.

    Also, if you have any quick questions that don't make good material for a stackoverflow questions, be sure to visit us over in the official redux channel on the reactiflux discord server.