TiernO
TiernO

Reputation: 447

3 forms into one state using redux?

I'm playing around with redux for the first time and I'm finding it fairly confusing haha It's not as simple as it sounds. I have a react project where I have a form that is divided into 3 parts(components). Each of these components has its own state to save the form data of that component.

Here is where I was gonna implement redux.

My goal here is to store the state of all 3 components in a react store. So on submit of each of the 3 form parts, they will all be merged into one big state in a redux store then I can do with it as I please.

Right now I have installed redux, created a store and now I'm lost haha Can anyone help or even direct me to a useful resource? I've looked at the redux documentation and just cannot get my head around it

My form:

return (
            <Form onSubmit={this.handleSubmit} className="form">
                {/* General Information */}
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="text" onChange={this.handleChange} name="siteName" id="siteName" placeholder={this.placeholders.siteName}/>
                    </Col>
                    <Col sm={6}>
                        <Input className="form-control" type="select" id="counties" onChange={this.handleChange}>
                            <option className="selectDefault" disabled value={this.placeholders.siteCounties} selected>{this.placeholders.siteCounty}</option>
                            { this.counties.map(c => (<option key={c.value} value={c.value}>{c.display}</option>))}
                        </Input>
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" onChange={this.handleChange} name="siteAddress" placeholder={this.placeholders.siteAdd} id="siteAddress" />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="email" name="siteEmail" onChange={this.handleChange} id="siteEmail" placeholder={this.placeholders.email} />
                    </Col> 
                    <Col sm={6}>
                        <Input type="tel" name="siteNumber" onChange={this.handleChange} id="siteNumber" placeholder={this.placeholders.number}/>
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="select" name="siteCat" onChange={this.handleChange} id="siteCat" multiple placeholder={this.placeholders.categories}>
                            <option className="selectDefault" disabled selected>{this.placeholders.categories}</option>
                            { this.categories.map(c => (<option key={c.value} value={c.value}>{c.display}</option>))}
                        </Input>
                    </Col>
                    <Col sm={6}>
                        <Input type="textarea" name="openTimes" onChange={this.handleChange} id="openTimes" placeholder={this.placeholders.times} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="textarea" name="fees" onChange={this.handleChange} id="fees" placeholder={this.placeholders.fees}/>
                    </Col>
                    <Col sm={6}>
                        <Input type="text" name="access" onChange={this.handleChange} id="access" placeholder={this.placeholders.access} />
                    </Col>
                </FormGroup>
                <hr/>
                {/* Location Information */}
                <FormGroup row> 
                    <Col sm={6}>
                        <Input type="text" name="gps" onChange={this.handleChange} id="gps" placeholder={this.placeholders.gps}/>
                    </Col>
                    <Col sm={6}>
                        <Input type="text" name="w3w" id="w3w" onChange={this.handleChange} placeholder={this.placeholders.w3w} />
                    </Col>
                </FormGroup>
                <hr/>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" name="txtHeader" onChange={this.handleChange} id="txtHeader" placeholder={this.placeholders.textHeader} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" name="txtContent" onChange={this.handleChange} id="txtContent" placeholder={this.placeholders.textContent} />
                    </Col>
                </FormGroup>
                <FormGroup check row>
                    <Col sm={{ size: 10, offset: 2 }}>
                        <Button disabled={!this.validateForm()} type="submit" className="btn-primary">Tours &rarr;</Button>
                    </Col>
                </FormGroup> 
            </Form>
        );

Form state:

constructor(props) {
        super(props);

        this.state = {
            language: this.props.language,
            siteName: '',
            counties: '',
            siteAddress: '',
            siteEmail: '',
            siteNumber: '',
            siteCat: '',
            openTimes: '',
            fees: '',
            access: '',
            gps: '',
            w3w: '',
            txtHeader: '',
            txtContent: '',
            isLoading: false
        };

    }

    validateForm() {
        if (this.state.siteName != '' &&
            this.state.siteAddress != '' &&
            this.state.siteEmail != '' &&
            this.state.siteNumber != '' &&
            this.state.openTimes != '' && 
            this.state.fees != '' && 
            this.state.access != '' && 
            this.state.gps != '' && 
            this.state.w3w != '' && 
            this.state.txtHeader != '' && 
            this.state.txtContent != '') {
                return true;
            } else {
                return false;
            }
    }

    handleChange = e => {
        this.setState({ ...this.state, [e.target.name]: e.target.value });
        console.log(this.state);
    }

Upvotes: 0

Views: 76

Answers (2)

Jordan Enev
Jordan Enev

Reputation: 18674

I would recommend you to take a look at redux-form.

As you already have redux as dependency, you can take advantage of all form's abstractions these redux-form already implemented it for us.

According to your use-case, redux-form already implemented such form behavior, knows as Wizard Form.

Here's a complete working example: https://redux-form.com/8.1.0/examples/wizard/

Upvotes: 0

Hasan Al-Natour
Hasan Al-Natour

Reputation: 2066

redux is a predictable state container that acts as your single point of truth, manipulating your data can be done using actions that are pure functions.

how redux works

so when you have a state that is shared between your components this is where you need to move this state to redux store, each item in the store has a reducer unless your app is only one thing.

for example, the first thing you need to do is install the needed dependencies :

npm install redux react-redux --save

after its installed create a file named store.js with the following code :

import {combineReducers,createStore} from 'redux'

// Your Reducer
import myReducer from './myreducer'

// we use combineReducers to be able to add more than one.
const reducers = combineReducers({
    mystate:myReducer
})


export default createStore(reducers);

then let's create that reducer myreducer.js:

// this is your pure function has 2 params
// first one is the state it immutable 
// then the action which is the action you dispatched with its paylod
const myReducer = (state = [], action) => {

    switch (action.type) {
        case "ADD_ITEM":
            // i created a new array and spread the data in it to be immutable 
            return [...state,action.item]
        // you always need this becuse redux will run all the reducers.    
        default:
            return state;
    }

 }

export default myReducer;

Now get back to the App.js component and make it as the following :

/*
The <Provider /> makes the Redux store available to any nested components that have been wrapped in the connect() function.

Since any React component in a React Redux app can be connected, most applications will render a <Provider> at the top level, with the entire app’s component tree inside of it.

Normally, you can’t use a connected component unless it is nested inside of a <Provider>.

*/
import React, {Component} from 'react';
import {Provider} from 'react-redux'
import store from './store'

export default class App extends Component {
    render(){
        return (
         <Provider store={store}>
         // your app
         </Provider>
        )
    }
}

i recommend you to go thrught the quick start (https://react-redux.js.org/introduction/quick-start)

Upvotes: 0

Related Questions