reactjs
Wrapping a TextField in my own reusable component
So I have a component that adds a * at the beginning of typed in text that looks like this: export default class MyTextField extends Component { constructor() { super(); this.state = { value: '', }; } static propTypes = { onChange: PropTypes.func, value: PropTypes.string } addSymbol(newValue, symbol) { return newValue.indexOf(symbol) === -1 ? `${symbol}${newValue}` : newValue; } change(input, newValue) { console.log(`I am the parent wit newValue ${newValue}.`); this.setState({ value: this.addSymbol(newValue, '*') }); } render() { const { onChange, // eslint-disable-line no-unused-vars ...other } = this.props; return ( <TextField value={this.state.value} onChange={this.change.bind(this)} {...other } > </TextField > ); } } Now I use MyTextFieldin another component like this: <MyTextField style={styleTextFields} floatingLabelText='Add text' type='text' /> Now when I start typing everything works great. If I change the code above and add value='test' prop on MyTextField everything breaks. It appears its because I added a prop to TextField which can not be overwritten by MyTextField value={this.state.value}. So how can I add prop value and still add a * at the beginning of the typed text? The other part is if the user adds their own onChange, how can I get both MyTextField and the component using MyTextField onChange events to get called? =======Beginning of Edit 1=============== #Patrick - I have made tweaks based on your comment and the first part is now working. So I can type in <TextField /> and I see the changes. The changes are below: export default class MyTextField extends Component { constructor(props) { super(); this.state = { value: props.value ? this.addSymbol(props.value.toString(), '*') : '' }; } static propTypes = { onChange: PropTypes.func, value: PropTypes.string } addSymbol(newValue, symbol) { return newValue.indexOf(symbol) === -1 ? `${symbol}${newValue}` : newValue; } change(input, newValue) { console.log(`I am the parent wit newValue ${newValue}.`); this.setState({ value: this.addSymbol(newValue, '*') }); } render() { const { value, // eslint-disable-line no-unused-vars onChange, // eslint-disable-line no-unused-vars ...other } = this.props; return ( <TextField value={this.state.value} onChange={this.change.bind(this)} {...other } > </TextField > ); } } I am not sure how to make the onChange tweak because you are not supposed to modify component state? I also did try this in the render() function which does not work: addSymbol(newValue, symbol) { return newValue.indexOf(symbol) === -1 ? `${symbol}${newValue}` : newValue; } change(input, newValue) { console.log(`I am the parent wit newValue ${newValue}.`); this.setState({ value: this.addSymbol(newValue, '*') }); } render() { const { value, // eslint-disable-line no-unused-vars onChange, // eslint-disable-line no-unused-vars ...other } = this.props; return ( <TextField value={this.addSymbol(this.state.value, '*')} {...other } > </TextField > ); } Anyway, thank you for your help. =======Ending of Edit 1=============== =======Beginning of Edit 2=============== I fixed the onChange event problem by adding my own event called onNewValueChange and calling it in the onChange. Here is the final code: export default class MyTextField extends Component { constructor() { super(); this.state = { value: props.value ? this.addSymbol(props.value.toString(), '*') : '' }; } static propTypes = { onNewValueChange: PropTypes.func, value: PropTypes.string } addSymbol(newValue, symbol) { return newValue.indexOf(symbol) === -1 ? `${symbol}${newValue}` : newValue; } change(input, newValue) { const value = value: props.value ? this.addSymbol(props.value.toString(), '*') : ''; this.setState({ value: value }); if(this.props.onNewValueChange) { this.props.onNewValueChange(value); } } render() { const { onNewValueChange, // eslint-disable-line no-unused-vars ...other } = this.props; return ( <TextField value={this.state.value} onChange={this.change.bind(this)} {...other } > </TextField > ); } } Now I use MyTextField in another component like this: <MyTextField style={styleTextFields} floatingLabelText='Add text' type='text' onNewValueChange={(newValue) => console.log(newValue) } /> #Patrick thank you for your help getting me in the right direction. =======Ending of Edit 2===============
In general, I think this pattern of omitting props before passing everything else inside a component is confusing, at least for me. Being explicit as possible should always be a priority. If I understood you correctly, the expected behavior is as follows: You can pass a default text, this text should be prefixed with a * by your component. You can modify the text, but no matter how you modify it, it should always be prefixed with a * - so an empty string should just be *. I've created this fiddle which hopefully does what you meant : https://jsfiddle.net/69z2wepo/78041/ The key concept is to separate between props and state, a prop is passed only once, and should really be responsible for the initial state of your Here's the two classes I created : class Input extends React.Component{ render(){ return (<input onChange={this.props.onChange} value={this.props.value}/>) } } class WrappedInput extends React.Component{ constructor(props){ super(); let val = props.value || ''; this.state = { value : this.addSymbol(val,'*') } } handleChange(e){ let newVal = e.target.value; this.setState({value:this.addSymbol(newVal,'*')}); } addSymbol(newValue, symbol) { return newValue.indexOf(symbol) === -1 ? `${symbol}${newValue}` : newValue; } render(){ return (<Input onChange={this.handleChange.bind(this)} value={this.state.value}/>) } }
Related Links
ReactJS component not rerendering when props are passed
React Redux … syntax error
How does one set the tabIndex attribute on a draft-js instance?
ComponentDidMount() not working
UIScrollView Canvas Equivalent?
ReactJS - props or propTypes?
React Router V4 structure
Should a view be ordering data?
Unable to load React class component inside other class component
How to get history on react-router v4?
Redux react immutable setIn element changes whole list
react: fade-in/slide-in animation when render component
ReactJS component vendors
css-loader, sass-loader not working webpack 2
The best way to organize your site ready for both PC and SP in reactjs, responsive or 2 sources?
Check data has already been fetched for react-redux in action, reducer or component?