Ilja
Ilja

Reputation: 46479

Passing jsx content to redux state and rendering it from there

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.

Upvotes: 5

Views: 2872

Answers (2)

Nicolai
Nicolai

Reputation: 1

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!

Upvotes: 0

laurent
laurent

Reputation: 90766

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>
)

Upvotes: 2

Related Questions