Skip to content

call js function with context #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tadjik1 opened this issue Oct 8, 2015 · 5 comments
Closed

call js function with context #12

tadjik1 opened this issue Oct 8, 2015 · 5 comments

Comments

@tadjik1
Copy link

tadjik1 commented Oct 8, 2015

Hello. Thanks for great component!

I have a problem with calling js callback function in context of my parent component:

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import ReactSWF from 'react-swf';

import './style.sass';

class Player extends Component {
    static propTypes = {
        url: PropTypes.string.isRequired,
        style: PropTypes.shape({
            position: PropTypes.oneOf([
                'absolute',
                'fixed'
            ]).isRequired,
            top: PropTypes.string,
            left: PropTypes.string,
            bottom: PropTypes.string,
            right: PropTypes.string,
            width: PropTypes.string.isRequired,
            height: PropTypes.string.isRequired
        }).isRequired
    };

    constructor(props) {
        super(props);
    }

    onJSBridge(...args) {
        console.log(this);
        console.log(args);
    }

    render() {
        const { style } = this.props;

        return (
            <div className="player-container" style={style}>
                <ReactSWF
                    src={'/player/GrindPlayer.swf'}
                    id={'player'}
                    ref={'player'}
                    name={'player'}
                    width={style.width}
                    height={style.height}
                    wmode={'transparent'}
                    flashVars={{javascriptCallbackFunction: this.onJSBridge}} />
            </div>
        );
    }
}

const select = state => state.player;

export default connect(select)(Player);

method onJSBridge allways call in window context. But if I pass flashVars={{javascriptCallbackFunction: this.onJSBridge.bind(this)}} I see error:

Uncaught SyntaxError: Unexpected identifier in console

How could I call method of object?

@syranide
Copy link
Owner

syranide commented Oct 8, 2015

See #9

ReactSWF is not involved in this and there's nothing ReactSWF can do that isn't opinionated/intrusive and would require special code built into the SWF.

Flash Player can only call functions exposed on window, so you're left with two choices:

  1. Each instance should store a uniquely named function on window, say my_swf_cb_123456(). Then pass the function name in flash-vars to the SWF.
  2. Export a named function on window with an extra ID parameter, say my_swf_cb(id). Each instance should generate a unique ID and store the ID and instance in a map used by my_swf_cb. Then pass the ID in flash-vars to the SWF.
  3. EDIT If there's only ever once instance, just store a bound callback on window and call that from the SWF.

I'm refactoring ReactSWF a bit and I may end up providing this functionality through a separate complementary module, but due to the design of FlashPlayer this is not something ReactSWF should provide.

EDIT: You cannot pass functions through flashVars because it has to be serialized into a string and passed to FlashPlayer.

@syranide
Copy link
Owner

syranide commented Oct 8, 2015

Hmm... it could make sense to have ReactSWF detect functions passed in flash-vars and special-case them perhaps. I'll think about this.

@syranide
Copy link
Owner

syranide commented Oct 8, 2015

Oh right facebook/react#4000 prevents this from being a good idea at the moment.

@syranide
Copy link
Owner

syranide commented Oct 9, 2015

const SWF_ID_PREFIX = '__MyExternalInterfaceExample_SWFID_';
const SWF_CALL_NAME_PREFIX = '__MyExternalInterfaceExample_SWFCall_';

let nextUID = 0;

class MyExternalInterfaceExample extends React.Component {
  constructor(props) {
    super(props);

    // For most purposes nextUID is sufficient. However, if you rely on
    // non-trivial server rendering you must generate deterministic UIDs per
    // React root to avoid markup mismatch.
    this._uid = nextUID++;

    window[SWF_CALL_NAME_PREFIX + this._uid] = this.handleSWFCall.bind(this);
  }

  componentWillUnmount() {
    delete window[SWF_CALL_NAME_PREFIX + this._uid];
  }

  handleSWFCall() {
    // Beware; SWF calls are executed in the context of SWF Player.
    console.log('SWFCall', arguments);
    return 'foobar';
  }

  invokeSWFMyCallback(arg) {
    // Beware; SWF Player does not sufficiently escape serialized arguments.
    return this._swfPlayerNode.myCallback(arg);
  }

  render() {
    // Globally unique ID is required for ExternalInterface callbacks in IE<11.
    return (
      <ReactSWF
        ...
        ref={c => this._swfPlayerNode = c}
        id={SWF_ID_PREFIX + this._uid}
        flashVars={{myCallbackName: SWF_CALL_NAME_PREFIX + this._uid}}
      />
    );
  }
}

@tadjik1
Copy link
Author

tadjik1 commented Oct 10, 2015

thanks a lot!

@tadjik1 tadjik1 closed this as completed Oct 10, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants