Files
react-from-zero/13-element-refactor.html
2016-04-28 22:19:26 +02:00

105 lines
2.9 KiB
HTML

<!doctype html>
<title>12 Component Refactor - React From Zero</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<div id='app'></div>
<script type="text/babel">
// Refactoring an element is a bit more tricky
// First, React decides if a tag is an element by checking its case
// lower case means element
// upper case means component
var element = <div/>
// becomes
element = React.createElement("div", null)
try {
var component = <Div/>
// becomes
component = React.createElement(Div, null)
} catch(e) {}
// Second, React converts all events, these elements trigger, to
// synthetic events. This is often not a problem, they are simply events.
// But you can't trigger your own.
// So even if your <Input> component accepts a onClick callback as property
// You can't call it with the same event as an <input> element would
// One approach could be this.
// We simply implement our own onChange caller
// Here we create a number input that only calls onChange on number inputs
// (non-numbers trigger an empty change)
var NumberInput = React.createClass({
getInitialState: function() {
return {value: ''}
},
handleInput: function(e) {
// we could try to modify the event to get or data in
// but this could mess things up
// instead we prevent this event from further actions
e.preventDefault()
var newNumber = e.target.value
// filter empty-changes
if (newNumber.length < 1 || newNumber === this.state.value) return
this.setState({value: newNumber})
//then we extract our data and give it to onChange
this.props.onChange(newNumber)
},
render: function () {
return <input type='number' value={this.state.value} onChange={this.handleInput}/>
},
})
function logChange(v){
console.log(v)
}
// Here we see, that the new NumberInput has a different interface
// it's onChange property implies that events will be received, but this isn't
// the case. Also, even if we would want to call it like the original input,
// we would need to use upper case, and wouldn't win anything
var reactElement = <div style={{width: 300, margin: 'auto'}}>
<h2>Logging number inputs</h2>
<h2>Before Refactor</h2>
<input type='number' onChange={function(e) { logChange(e.target.value) }}/>
<h2>After Refactor</h2>
<NumberInput onChange={logChange}/>
</div>
ReactDOM.render(reactElement, document.getElementById('app'))
// Other approaches include not using "default" prop names in the first place
// onUpdate instead of onChange
// It could also that a component uses onMouseDown to do something internall
// and triggers a onChange, which could cause confusion
// Often components deliver richer interactions than elements in the first place
// so their prop methods can reflect that with the name
</script>