I have found a case where two very similar functions that output exactly the same DOM element, work in 2 different ways. Am I missing something? The only difference is I moved the DEFS block from the render function to the renderDefs function.
var mainView = React.createClass({
getInitialState: function () {
return {
showDef: false
}
},
changeDefs: function () {
this.setState({
showDef: true
})
},
renderDefs: function () {
if (!this.state.showDef) return null;
return (
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stopColor="white"/>
<stop offset="95%" stopColor="blue"/>
</linearGradient>
</defs>
);
},
render: function () {
var fillStyle = this.state.showDef ? {fill: "url(#Gradient1)"} : {fill: "red"};
return (
<svg viewBox="0 0 1024 700" onClick={this.changeDefs}>
{this.renderDefs()}
<rect x="0" y="0" height="50" width="50" style={fillStyle}/>
</svg>
);
}
// FIXME, THIS VERION DOESN'T WORK
// NO CLUES WHY
// renderDefs: function () {
// if (!this.state.showDef) return null;
// return (
// <linearGradient id="Gradient1">
// <stop offset="5%" stopColor="white"/>
// <stop offset="95%" stopColor="blue"/>
// </linearGradient>
// );
// },
//
// render: function () {
// var fillStyle = this.state.showDef ? {fill: "url(#Gradient1)"} : {fill: "red"};
// return (
// <svg viewBox="0 0 1024 700" onClick={this.changeDefs}>
// <defs>
// {this.renderDefs()}
// </defs>
// <rect x="0" y="0" height="50" width="50" style={fillStyle}/>
// </svg>
// );
// }
});
null in React actually renders to <noscript />. It seems that <noscript> inside <defs> is not allowed.
I tried several different combinations. One that troubled me was the one below. When I add to the props.patterns the first pattern object, it works properly. When I add the second one, the first pattern still works, but the second doesn't... It feels like when inserting the second "pattern" node its not using the proper DOM constructor, but I still might be missing something
renderDefs: function () {
var that = this;
var defsArray = [];
var patternIds = Object.keys(this.props.patterns);
if (patternIds.length === 0) return
patternIds.forEach(function (patternId) {
var patternInfo = that.props.patterns[patternId];
defsArray.push(
<pattern id={patternId} patternUnits="userSpaceOnUse" width="4" height="4">
<path d="M -1,2 l 6,0" stroke={patternInfo.color} strokeWidth="1"/>
</pattern>
);
});
return (
<defs>
{defsArray}
</defs>
);
}
What does your render function look like for the last example of renderDefs you gave ?
Let me paste the whole module, i changed this.props with this.state
var mainView = React.createClass({
getInitialState: function () {
return {
patterns: {}
}
},
testColors: ['red', 'blue', 'yellow', 'green'],
changeDefs: function () {
var newColor = this.testColors.shift();
var patterns = this.state.patterns;
patterns[newColor] = {
color: newColor
};
this.setState({
patterns: patterns
})
},
renderDefs: function () {
var that = this;
var defsArray = [];
var patternIds = Object.keys(this.state.patterns);
if (patternIds.length === 0) return;
patternIds.forEach(function (patternId) {
var patternInfo = that.state.patterns[patternId];
defsArray.push(
<pattern id={patternId} patternUnits="userSpaceOnUse" width="4" height="4">
<path d="M -1,2 l 6,0" stroke={patternInfo.color} strokeWidth="1"/>
</pattern>
);
});
return (
<defs>
{defsArray}
</defs>
);
},
render: function () {
var fillStyle;
var patternIds = Object.keys(this.state.patterns);
if(patternIds.length > 0){
var patternId = patternIds[patternIds.length -1];
fillStyle = {fill: "url(#" + patternId + ")"};
} else { fillStyle = {fill: "red"}; }
return (
<svg viewBox="0 0 1024 700" onClick={this.changeDefs}>
{this.renderDefs()}
<rect x="0" y="0" height="50" width="50" style={fillStyle}/>
</svg>
);
}
});
Sorry for these verbose comments... If you in the inspector element, and manually change the fill back to #red it works, the other colors don't
@taddei Tried out your testcase, can corroborate your findings. The only way I've managed to make the update work is forcing React to completely alter the SVG element's parent (jsbin), which is an ugly, ugly hack.
@ToucheSir yes, thanks. We were just prototyping and this issue came out. It happens only when react has to "add" elements to the svg. I don't have expertise on the core React functionality, but I would guess that it would be the usage of innerHTML instead of innerSVG. But it's just a guess.
FYI for any other internet denizens coming upon weird <defs> issues when adding elements dynamically to an <svg>, the key (pun intended) for me was to add a dynamic key property to <defs> so that react properly catches and schedules the update for the parent element itself.
Can you please prepare a fiddle that demonstrates the issue and add steps to reproduce it? Unfortunately I can鈥檛 figure out what to do with the fiddle in https://github.com/facebook/react/issues/2380#issuecomment-59807316. Thanks!
Whoops, never mind, I reproduced it in 0.14.
This appears fixed in v15 RC2: http://jsbin.com/getiwetolo/1/edit
I鈥檓 closing but please let me know if I missed something.
Most helpful comment
FYI for any other internet denizens coming upon weird
<defs>issues when adding elements dynamically to an<svg>, the key (pun intended) for me was to add a dynamickeyproperty to<defs>so that react properly catches and schedules the update for the parent element itself.