5

I am having trouble figuring out how to pass JSX to my redux state i.e. for modal component that is used globally and has its redux state where one parameter is content such content can be updated to include JSX code.

At the moment I am getting it to render correct content however it doesn't seem that functions are called correctly and I am also getting following error:

invariant.js:38 Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, isDefaultPrevented, isPropagationStopped, _dispatchListeners, _dispatchInstances, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, screenX, screenY, clientX, clientY, ctrlKey, shiftKey, altKey, metaKey, getModifierState, button, buttons, relatedTarget, pageX, pageY}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of styled.div.

With a lot of following errors:

warning.js:36 Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property nativeEvent on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See https://fbme(replaced this as so doesn't allow links to fb)/react-event-pooling for more information.

Example implementation:

Function called from a page to show modal and add contents to it

  onToggleModal = () => {
    this.props.modalToggle(
      (<TopUp account={getSession().accounts[0] || {}} />)
    );
  }

Where this.props.modalToggle is a redux action like this:

export const modalToggle = (content = '') => ({
  type: MODAL_TOGGLE,
  payload: content
});

I then try to render such content inside my Modal container:

return (
 <div>{this.props.content}</div>
)

I imported React into my reducer, in hopes to resolve jsx issues, but had no luck. It also seems like components are passed to reducer as some sort of weird objects.

Ilja
  • 44,142
  • 92
  • 275
  • 498
  • If you are looking for a `redux` pattern with modals, I highly suggest this approach:http://stackoverflow.com/questions/35623656/how-can-i-display-a-modal-dialog-in-redux-that-performs-asynchronous-actions We use it in production currently and it's very stable. – lux Dec 15 '16 at 21:43
  • @lux looks good, I am having difficulties passing functions as props though, any advice? – Ilja Dec 16 '16 at 11:09

2 Answers2

2

Redux state can only contain plain objects and data types, so a JSX object would probably cause issues. Also it's most likely not a good idea to have a JSX object (which is basically a view) as part of your state.

Instead, you should pass around whatever data is required to render the final view. I think this would work:

onToggleModal = () => {
  this.props.modalToggle(getSession().accounts[0] || {});
}

export const modalToggle = (account = {}) => ({
  type: MODAL_TOGGLE,
  account: account
});

return (
 <div><TopUp account={account} /></div>
)
laurent
  • 88,262
  • 77
  • 290
  • 428
  • 2
    But say we have a more complex example where modal should display different components that are not necessarily same for another Modal view. The only way I can think of to do this is have all of these views predefined in Modal container and pass data to it via redux like you suggested and some sort of TYPE parameter to tell it which view to render, could be done, but seems like a very code heavy implementation, hence asking this question here. If no better answer pops up I guess I will have to go with it. – Ilja Dec 15 '16 at 15:27
0

Hey it's been a while now but for the record I stumbled upon the same use case you are explaining in the reply to the accepted answer.

You can achieve this by adding a property of JsxElement | ReactElement | HTMLElement type in your modalSlice state:

export interface modalState {
  active: boolean;
  component?: ReactElement;
}

reducers: {
   updateModalComponent: (state, value: PayloadAction<ReactElement>) => {
      state.component = value.payload;
   },
}

Then your modal component file could look something like this:

import { ReactElement } from 'react';
import './modal.scss';

interface Props {
    active: boolean,
    children: ReactElement | JsxElement | HTMLElement,
}

export const Modal = (props: Props) => {
    return (
        <div id="modal-container">
            {
                props.active &&
                <div id="overlay">
                    <div id="modal-content-container">
                        {props.children}
                    </div>
                </div>
            }
        </div>
    )
}

Finally use it anywhere!

const element = <OtherComponent />;
dispatch(updateModalComponent(element));

Cheers!