React-redux mapStateToProps values not updated

Viewed 270

I have a weird scenario in which inside my redux dev-tools I can see that the redux state has been updated but that value is not being reflected in the component which is connected to that.

So I have a component CreateEnvironment like

export interface StateProps {
  environment: EnvironmentStep;
  selectProduct: SelectProductStep;
  eula: EULAStep;
  license: LicenseStep;
  infrastructure: InfrastructureStep;
  network: NetworkStep;
  certificate: CertificateStep;
  products: any;
}

export interface DispatchProps {
  onFormValuesUpdate: (key: string, value: any) => void;
}

type Props = StateProps & DispatchProps;

interface State {
  dashboard: boolean;
}

class CreateEnvironment extends Component<Props, State> {
  private page: RefObject<any>;
  constructor(props: Props) {
    super(props);
    this.state = { dashboard: false };
    this.page = React.createRef();
  }
  public render() {
    console.log("Hi"); //tslint:disable-line
    if (this.state.dashboard) {
      return <Redirect to="/dashboard" />;
    }
    const {
      environment,
      eula,
      selectProduct,
      infrastructure,
      network,
      license,
      certificate,
      onFormValuesUpdate
    } = this.props;
    return (
      <Fragment>
        <h2>Create Environment</h2>
        <div style={{ height: '100%', paddingBottom: '30px' }}>
          <WorkFlow
            orientation="vertical"
            onNext={this.onNext}
            onFinish={this.onFinish}
          >
            <WorkFlowPage pageTitle="Environment">
              <Environment
                environment={environment}
                onUpdate={onFormValuesUpdate}
                ref={this.page}
              />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="Products & Solutions">
              <SelectProduct
                ref={this.page}
                selectProduct={selectProduct}
                onUpdate={onFormValuesUpdate}
              />
            </WorkFlowPage>
            <WorkFlowPage
              pageNavTitle="EULA"
              pageTitle="End User License Agreement Details"
            >
              <Eula eula={eula} ref={this.page} onUpdate={onFormValuesUpdate} />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="License">
              <License
                license={license}
                ref={this.page}
                onUpdate={onFormValuesUpdate}
              />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="Infrastructure">
              <Infrastructure
                infrastructure={infrastructure}
                ref={this.page}
                onUpdate={onFormValuesUpdate}
              />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="Network">
              <Network
                network={network}
                ref={this.page}
                onUpdate={onFormValuesUpdate}
              />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="Certificate">
              <Certificate
                certificate={certificate}
                ref={this.page}
                onUpdate={onFormValuesUpdate}
              />
            </WorkFlowPage>
            <WorkFlowPage pageTitle="Products">I am products step</WorkFlowPage>
            <WorkFlowPage pageTitle="Summary">I am summary step</WorkFlowPage>
            <WorkFlowButton role="previous">Back</WorkFlowButton>
            <WorkFlowButton role="next">Next</WorkFlowButton>
            <WorkFlowButton role="finish">Finish</WorkFlowButton>
          </WorkFlow>
        </div>
      </Fragment>
    );
  }

  private onNext = () => {
    if (this.page.current) {
      this.page.current.handleSubmit();
    }
  }

  private onFinish = () => {
    this.setState({
      dashboard: true
    });
  }
}

export default CreateEnvironment;

The corresponding container looks like:

import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import {
  CreateEnvironmentAction,
  updateEnvironmentWorkflow
} from '../actions/CreateEnvironment';
import {
  CreateEnvironment,
  DispatchProps,
  StateProps
} from '../components/create-environment';
import { StoreState } from '../types';
export const mapStateToProps = ({
  createEnvironment
}: StoreState): StateProps => ({
  ...createEnvironment
});

export const mapDispatchToProps = (
  dispatch: Dispatch<CreateEnvironmentAction>
): DispatchProps => ({
  onFormValuesUpdate: (key: string, values: any) => {
    dispatch(updateEnvironmentWorkflow(key, values));
  }
});

export default connect<StateProps, DispatchProps>(
  mapStateToProps,
  mapDispatchToProps
)(CreateEnvironment);

All the components inside WorkflowPage like Environment are forms which use the Formik pattern. A sample component is:

interface Props {
  certificate?: CertificateStep;
  onUpdate?: (key: string, value: any) => void;
}
class Certificate extends Component<Props> {
  private submitForm: (
    e?: React.FormEvent<HTMLFormElement> | undefined
  ) => void;
  public render() {
    const { formValues } = this.props.certificate!;
    console.log(formValues); //tslint:disable-line
    return (
      <Form
        className="license"
        type="horizontal"
        initialValues={formValues}
        onSubmit={this.onSubmit}
      >
        {formProps => {
          this.submitForm = formProps.handleSubmit;
          return (
            <Fragment>
              <CheckboxField
                name="productSpecific"
                id="productSpecific"
                type="toggle"
              >
                Provide Product Specific Certificate
              </CheckboxField>
              <SelectField name="certificate" label="Certificate">
                <SelectOption value="">---Select Certificate---</SelectOption>
              </SelectField>
            </Fragment>
          );
        }}
      </Form>
    );
  }

  public handleSubmit = () => {
    this.submitForm();
  }

  private onSubmit = (values: FormValues, actions: FormActions) => {
    this.props.onUpdate!('certificate', values);
    actions.setSubmitting(false);
  }
}

export default Certificate;

On clicking the next WorkflowButton the redux state is updated by passing an action updateEnvironmentWorkflow. The action is like:

export interface UpdateEnvironmentWorkflow {
  type: Constants.UPDATE_ENVIRONMENT_WORKFLOW;
  payload: {
    key: string;
    value: any;
  };
}

export type CreateEnvironmentAction = UpdateEnvironmentWorkflow;

export const updateEnvironmentWorkflow = (
  key: string,
  value: any
): UpdateEnvironmentWorkflow => ({
  payload: { key, value },
  type: Constants.UPDATE_ENVIRONMENT_WORKFLOW
});

And my reducer is like:

const createEnvironment = (
  state: CreateEnvironment = initialState.createEnvironment,
  action: CreateEnvironmentAction
) => {
  switch (action.type) {
    case Constants.UPDATE_ENVIRONMENT_WORKFLOW:
      return {
        ...state,
        [action.payload.key]: {
          ...state[action.payload.key],
          formValues: action.payload.value
        }
      };
    default:
      return state;
  }
};

export default createEnvironment;

Now the weird part is that if I click the next button the redux state is updated with new values. If I navigate to some other page and come back to create environment my form values are showing the updated values from the redux store state.

BUT if I just hit the previous button my state values are not showing updated values. The console.log(formValues) inside render() of Certificate is showing initial values only.

Any help will be appreciated. Thanks.

Also initialValues of Form is Formik initialValues. It maybe a formik issue as well

0 Answers
Related