Skip to content
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

share-js with React #63

Open
hennoxlane opened this issue Mar 19, 2016 · 2 comments
Open

share-js with React #63

hennoxlane opened this issue Mar 19, 2016 · 2 comments

Comments

@hennoxlane
Copy link

I'm trying to use this template with ReactJS, by wrapping it in the blazetoreact package and rendering it that way.

I've got a documents collection, and am passing the mongo ID to docid.
Here's the component I'm using shareJS in:

const ShareJS = BlazeToReact('_sharejsText'); //need the underscore here, or the package isn't found
import Documents from '../../collections/Documents.js';

const App = React.createClass({
  mixins: [ReactMeteorData],
  getMeteorData() {
    let handle = Meteor.subscribe("documents");
    return {
      documentsLoading: ! handle.ready(),
      documents: Documents.find().fetch(),
    };
  },
  createDocument() {
    Documents.insert({
      title: "untitled"
    });
  },
  render: function() {
    if (!this.data.documentsLoading) {
      return (
          <ShareJS docid={this.data.documents[0]._id} id="editor" />
      );
    }
    return <div onClick={this.createDocument}>create document</div>;
  }
});

export default App;

The result is that once I click a button (other component) to insert a new document, I'm seeing a textarea rendered, with '....loading' in it. And that's it. No functionality.

Troubleshooting: I can see the ops collection being created in my mongo, but there's no sign of the docs collection.

I've been able to setup shareJS with blaze just fine, no issues there: a click on a button creates a new Document, set the id to docid on a Session variable & presto, I can see the docs collection getting created in mongo & I'm good to go.

So far, that seems to be the one thing that's different: I'm not using Session in React.

So I'm wondering: does this package depend on usage of Session? Or is there another way to trigger the creation of the shareJS docs collection?

If there's no way to use this in Meteor + React, I'll use the plugin as is in a Blaze template, but I'd rather stick to a uniform codebase, if I can.

update: I've tried setting the document on a session variables and passing that through the docid prop, but no luck so far. This is most likely something to do with data-context not begin set correctly, I guess?

@mizzao
Copy link
Owner

mizzao commented Mar 20, 2016

If you look at the code, it does make use of the Blaze APIs, so I don't think you can just drop it in React like that.

@vincentracine
Copy link

vincentracine commented May 9, 2016

Here a copy and paste from my AceEditor React component.

// AceEditor.js
AceEditor = React.createClass({

    getInitialState(){
        return {
            doc: null,
            editor: null
        }
    },

    componentDidMount(){
        // Get Ace Editor from DOM
        this.state.editor = ace.edit("editor");

        // Set Ace Editor behaviour
        this.setTheme('ace/theme/chrome');
        this.state.editor.getSession().setMode("ace/mode/python");
        this.state.editor.setFontSize(14);
        this.state.editor.setShowPrintMargin(false);
        this.state.editor.getSession().setUseWrapMode(true);
        this.state.editor.$blockScrolling = Infinity;
        this.state.editor.resize();

        this.onChange();
    },

    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.docid !== this.props.docid;
    },

    componentDidUpdate(){
        this.onChange();
    },

    componentWillUnmount(){

        // Disconnect from ShareJS
        this.disconnect();

        // Clean up Ace memory to avoid memory leaks
        this.state.editor.destroy();

    },

    // ------- ShareJS

    onChange(){

        // Doesn't have a document opened but wants to connect
        if(!this.isConnected() && this.props.docid){
            this.connect(this.props.docid);
        }

        // Has a document opened but wants to open a new document
        if(this.isConnected() && this.props.docid){
            this.disconnect();
            this.connect(this.props.docid);
        }

        // Has a document opened but wants to close it
        if(this.isConnected() && !this.props.docid){
            this.disconnect();
        }

    },
    connect(documentId){
        let self = this;

        if(this.isConnected()){
            throw new Error('Already connected to ShareJS');
        }

        // Open the document
        sharejs.open(documentId, 'text', function(error, doc){
            if(error) {
                console.error("Connection error:", error);
            }else{
                // Update state
                self.setState({ doc: doc });

                // Check we are connected
                if(self.isConnected()){
                    // Attach ace editor to document
                    doc.attach_ace(self.state.editor);
                    console.log('Opened document [',documentId,']');
                }else{
                    console.error("Document was opened but closed right away");
                }
            }
        });
    },
    disconnect(){
        if(this.isConnected()){
            let name = this.state.doc.name;
            this.state.doc.close();
            if(this.state.doc.state === 'closed'){
                console.log('Closed document [',name,']');
            }else{
                console.error('Failed to close document [',name,']');
            }
            this.state.doc.detach_ace();
        }
    },
    isConnected(){
        return this.state.doc != null && this.state.doc.state === 'open';
    },

    // ------- End of ShareJS


    // ------- Editor State

    setTheme(theme){
        this.state.editor.setTheme(theme);
    },
    getAceInstance(){
        return this.state.editor;
    },
    getText(){
        return this.state.editor ? this.state.editor.getValue() : null;
    },

    // ------- End of Editor State

    render() {
        return (
            <div id='editor' ref="editor" data-doc={this.props.docid} className="shareJSAce"></div>
        )
    }
});

And you can use it like this:

EditorPage = React.createClass({

    mixins: [ReactMeteorData],

    getMeteorData() {
        return {
            currentUser: Meteor.user(),
            document: Documents.findOne({_id: this.props.docId})
        }
    },

    ready(){
        return this.data.document;
    },

    render(){
        return (
            <Row class="container-fluid">
                <div className="col-xs-12">
                    {this.ready() ? (
                        <AceEditor ref="editor" docid={this.props.docId}/>
                    ) : null}
                </div>
            </Row>
        )
    }
});

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

3 participants