9

I need to build a visual editor for an HTML page. It seems that ReactJS is good choice for that. Currently I faced with the following problem:

I modeled my data:

var Model = {
    title: 'Hello',
    description: 'Pellentesque eleifend urna ac purus tempus...',
    date: new Date().toString()
};

And built component which stores its data inside the above structure:

var App = React.createClass({
    getInitialState: function () {
        return {
            value: Model
        }
    },
    handleMouseEnter: function (event) {
        $(event.target).css('outline', '1px solid red').attr('contenteditable', true);
    },
    handleMouseLeave: function (event) {
        var t = $(event.target), v = this.state.value;
        t.css('outline', 'none').attr('contenteditable', false);
        v[t.attr('property')] = t.text();
        this.setState({value: v});
    },
    render: function () {
        var v = this.state.value;
        return (
            <div>
                <pre style={{whiteSpace: 'normal'}}>{JSON.stringify(this.state)}</pre>
                <h1 onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} property="title">{v.title}</h1>
                <p onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} property="description">{v.description}</p>
                <p onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} property="date">{v.date}</p>
            </div>
        );
    }
});

React.renderComponent(<App />, document.getElementById('app'));

In that particular case it works. But I want to get rid of onMouseEnter and onMouseLeave properties and to remain only property field. Like that:

<pre style={{whiteSpace: 'normal'}}>{JSON.stringify(this.state)}</pre>
<h1 property="title">{v.title}</h1>
<p property="description">{v.description}</p>
<p property="date">{v.date}</p>

I thought about creating mixin. Then, inside componentDidMount event, attach handlers to all elements with property attribute. But I found no way to achieve this.

My question is: Is there a way to traverse tree built by React? I noticed that React Developer Tools (Chrome Extension) can do that.

Similar Question: React.js component creating parent child relations and iterating

Community
  • 1
  • 1
vbarbarosh
  • 3,502
  • 4
  • 33
  • 43
  • So you need a way to traverse the children components generated by the parent? In your parent, have a state that stores an array of your models. Then in the render, you can use ```array.map``` to iterate through each of those models in the array, and return a child component passing the eventHandlers and the model data from the parent – trekforever Oct 23 '14 at 18:13
  • 1
    @trekforever bad solution. With it I need to have one more model -- for View from which DOM nodes will be generated. http://jsfiddle.net/kb3gN/6684/ – vbarbarosh Oct 23 '14 at 18:57
  • I'm confused as to why you would want to do what you describe - perhaps further elaboration on what exactly you are trying to do that can't just be achieved with `array.map` ? – Mike Driver Oct 23 '14 at 22:22
  • https://github.com/Cirru/cirru-fractal-editor – jiyinyiyong Oct 24 '14 at 05:04
  • https://github.com/Cirru/cirru-editor I tried in two projects. Maybe it helps. – jiyinyiyong Oct 24 '14 at 05:04
  • 1
    Seems to me that you are trying to do something that React can do for you.. If you want to traverse all _children_ of this component, you simply use the `children` property. A better way, IMO, is to you just plain DOM methods in a mixin on `componentDidMount` where you can say something like `document.querySelectorAll('*[property]')` (non-jquery ex). Don't know if the selector actually is correct.. – Hulvej May 02 '15 at 16:30
  • 1
    Why are you changing the CSS with jquery instead of just having `h1.someClass:hover { outline: 1px solid red; }` in your CSS files? Also I've heard `contentEditable` doesn't play nicely with React, you might want to read about it here http://stackoverflow.com/questions/22677931/react-js-onchange-event-for-contenteditable/27255103#27255103 for example before continuing – Andy Jun 05 '15 at 19:47
  • Take a look at Higher Order Components https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e – T Mitchell Jan 05 '17 at 15:59

1 Answers1

0

So you want:

<h1 onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} property="title">{v.title}</h1>

as

<h1 property="title">{v.title}</h1>

I would use another Component for that, that takes care of the rendering of that specific type of components, like redux-form does.

The most simple approach to achieve what you want to do would be something like:

class MyField extends Component {
  constructor(props) { 
    super(props); 
    this.handleMouseEnter = this.handleMouseEnter.bind(this); 
  }
  handleMouseEnter() { ... }
  render() {
    const { property, children } = this.props;
    if (property === 'title') {
      return (
        <h1 onMouseEnter...>{children}</h1>
      );
    }
  }
}

Then you would call it like this on any other component:

<MyField property="title">Stackoverflow</MyField>

A more professional, but also more difficult approach, would be redux-form implementation of Field, on which they even use React.createElement to generate HTML (what I do with if-else in the example above).

If you need to access any of its data outside it, either you pass it a function through properties or connect that MyField component to redux (redux-form does this too).

I would also pass through props any specific one-time styles that you want to apply to your MyField components.

zurfyx
  • 31,043
  • 20
  • 111
  • 145