In this project we will create an application that can change the theme of a paragraph real-time using elements on the front-end. We will also create a switch that can enable and disable the ability to change the theme of the paragraph. In the process of creating this project, we will cover how to use this, bind, state, props, and componentWillReceiveProps.
You can find a finished live example here.
forkandclonethis repository.cdinto the project directory.- Run
npm install. - Run
npm startafternpm installhas finished.
In this step, we will create the initial state for src/App.js. On this state object, we will keep track of the initial theme values and if the theme is allowed to be edited.
- Open
src/App.js. - Create a
constructormethod where it says// constructor:- This method should call
super(). - This method should create an initial state object with the following properties:
- fontColor: 'black'
- fontSize: 12,
- fontFamily: 'monospace'
- allowEdit: 'true'
- This method should call
src/App.js
import React, { Component } from 'react';
// Components
import EditToggle from './components/EditToggle';
import ColorChanger from './components/ColorChanger';
import SizeChanger from './components/SizeChanger';
import FamilyChanger from './components/FamilyChanger';
import TextContainer from './components/TextContainer';
class App extends Component {
constructor() {
super();
this.state = {
fontColor: 'black',
fontSize: 12,
fontFamily: 'monospace',
allowEdit: 'true'
};
}
// updateColor
// updateSize
// updateFamily
// updateEditStatus
render() {
return (
<div>
<div className="headerBar">
{ /* Render EditToggle */ }
{ /* Render ColorChanger */ }
{ /* Render SizeChanger */ }
{ /* Render FamilyChanger */ }
</div>
<div className="textArea">
{ /* Render TextContainer */ }
</div>
</div>
)
}
}
export default App;In this step, we will create class methods in src/App.js to update fontColor, fontSize, fontFamily, and allowEdit on state.
- Open
src/App.js. - Create an
updateColormethod that takes a parameter calledvalwhere it says// updateColor:- Use
setStateto updatefontColortoval.
- Use
- Create an
updateSizemethod that takes a parameter calledvalwhere it says// updateSize:- Use
setStateto updatefontSizetoval.
- Use
- Create an
updateFamilymethod that takes a parameter calledvalwhere it says// updateFamily:- Use
setStateto updatefontFamilytoval.
- Use
- Create an
updateEditStatusmethod that takes a parameter calledvalwhere it says// updateEditStatus:- Use
setStateto updateallowEdittoval.
- Use
src/App.js
import React, { Component } from 'react';
// Components
import EditToggle from './components/EditToggle';
import ColorChanger from './components/ColorChanger';
import SizeChanger from './components/SizeChanger';
import FamilyChanger from './components/FamilyChanger';
import TextContainer from './components/TextContainer';
class App extends Component {
constructor() {
super();
this.state = {
fontColor: 'black',
fontSize: 12,
fontFamily: 'monospace',
allowEdit: 'true'
};
}
updateColor(val) {
this.setState({ fontColor: val });
}
updateSize(val) {
this.setState({ fontSize: val });
}
updateFamily(val) {
this.setState({ fontFamily: val });
}
updateEditStatus(val) {
this.setState({ allowEdit: val });
}
render() {
return (
<div>
<div className="headerBar">
{ /* Render EditToggle */ }
{ /* Render ColorChanger */ }
{ /* Render SizeChanger */ }
{ /* Render FamilyChanger */ }
</div>
<div className="textArea">
{ /* Render TextContainer */ }
</div>
</div>
)
}
}
export default App;In this step, we will bind this to our methods in the constructor method in App.js. We'll only need to bind this on the updateColor, updateSize, and updateFamily methods.
- Open
src/App.js. - Bind
thisto theupdateColor,updateSize,updateFamily, andupdateEditStatusmethods at the bottom of theconstructormethod.
src/App.js
import React, { Component } from 'react';
// Components
import EditToggle from './components/EditToggle';
import ColorChanger from './components/ColorChanger';
import SizeChanger from './components/SizeChanger';
import FamilyChanger from './components/FamilyChanger';
import TextContainer from './components/TextContainer';
class App extends Component {
constructor() {
super();
this.state = {
fontColor: 'black',
fontSize: 12,
fontFamily: 'monospace',
allowEdit: 'true'
};
this.updateColor = this.updateColor.bind( this );
this.updateSize = this.updateSize.bind( this );
this.updateFamily = this.updateFamily.bind( this );
this.updateEditStatus = this.updateEditStatus.bind( this );
}
updateColor(val) {
this.setState({ fontColor: val });
}
updateSize(val) {
this.setState({ fontSize: val });
}
updateFamily(val) {
this.setState({ fontFamily: val });
}
updateEditStatus(val) {
this.setState({ allowEdit: val });
}
render() {
return (
<div>
<div className="headerBar">
{ /* Render EditToggle */ }
{ /* Render ColorChanger */ }
{ /* Render SizeChanger */ }
{ /* Render FamilyChanger */ }
</div>
<div className="textArea">
{ /* Render TextContainer */ }
</div>
</div>
)
}
}
export default App;In this step, we will render our imported components in App.js.
- Open
src/App.js. - Render the
EditTogglecomponent where it says{ /* Render EditToggle */ }:- Add a prop called
updatethat is equal to theupdateEditStatusmethod.
- Add a prop called
- Render the
ColorChangercomponent where it says{ /* Render ColorChange */ }:- Add a prop called
updatethat is equal to theupdateColormethod. - Add a prop called
allowEditthat is equal to the value ofallowEditon state.
- Add a prop called
- Render the
SizeChangercomponent where it says{ /* Render SizeChanger */ }:- Add a prop called
updatethat is equal to theupdateSizemethod. - Add a prop called
allowEditthat is equal to the value ofallowEditon state.
- Add a prop called
- Render the
FamilyChangercomponent where it says{ /* Render FamilyChanger */ }:- Add a prop called
updatethat is equal to theupdateFamilymethod. - Add a prop called
allowEditthat is equal to the value ofallowEditon state.
- Add a prop called
- Render the
TextContainercomponent where it says{ /* Render TextContainer */ }:- Add a prop called
fontColorthat equalsfontColoron state. - Add a prop called
fontSizethat equalsfontSizeon state. - Add a prop called
fontFamilythat equalsfontFamilyon state.
- Add a prop called
src/App.js
import React, { Component } from 'react';
// Components
import EditToggle from './components/EditToggle';
import ColorChanger from './components/ColorChanger';
import SizeChanger from './components/SizeChanger';
import FamilyChanger from './components/FamilyChanger';
import TextContainer from './components/TextContainer';
class App extends Component {
constructor() {
super();
this.state = {
fontColor: 'black',
fontSize: 12,
fontFamily: 'monospace',
allowEdit: 'true'
};
this.updateColor = this.updateColor.bind( this );
this.updateSize = this.updateSize.bind( this );
this.updateFamily = this.updateFamily.bind( this );
this.updateEditStatus = this.updateEditStatus.bind( this );
}
updateColor(val) {
this.setState({ fontColor: val });
}
updateSize(val) {
this.setState({ fontSize: val });
}
updateFamily(val) {
this.setState({ fontFamily: val });
}
updateEditStatus(val) {
this.setState({ allowEdit: val });
}
render() {
return (
<div>
<div className="headerBar">
<EditToggle update={ this.updateEditStatus } />
<ColorChanger update={ this.updateColor } allowEdit={ this.state.allowEdit } />
<SizeChanger update={ this.updateSize } allowEdit={ this.state.allowEdit } />
<FamilyChanger update={ this.updateFamily } allowEdit={ this.state.allowEdit } />
</div>
<div className="textArea">
<TextContainer
fontColor={ this.state.fontColor }
fontSize={ this.state.fontSize }
fontFamily={ this.state.fontFamily } />
</div>
</div>
)
}
}
export default App;In this step, we will update our select elements in the EditToggle, ColorChanger, FamilyChanger, and SizeChanger components to use an onChange that calls the update prop with the value of the select element. We will also disable the select element if the value of allowEdit is "false".
- Open
EditToggle,ColorChanger.js,FamilyChanger.js, andSizeChanger.jsfromsrc/components/. - Locate the
selecttag, in all four files, and add anonChangeprop:- The
onChangeshould use an arrow function to capture theevent. - Inside the arrow function call the
updateprop with the value of the target from theevent. - Parse Int the value of the target when in
SizeChanger.js.
- The
- Locate the
selecttag, inColorChanger,FamilyChanger, andSizeChanger, and add adisabledprop:- The
selectelement should bedisabledifallowEditon state is equal to"false".
- The
src/components/EditToggle.js
render() {
return (
<select className="dropDownContainer ml0" onChange={ (e) => this.props.update(e.target.value) }>
<option value="true"> Allow Edit </option>
<option value="false"> Disable Edit </option>
</select>
)
} src/components/ColorChanger.js
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update(e.target.value) } disabled={ this.state.allowEdit === "false" }>
<option value="black"> Black </option>
<option value="blue"> Blue </option>
<option value="green"> Green </option>
</select>
)
} src/components/FamilyChanger.js
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update(e.target.value) } disabled={ this.state.allowEdit === "false" }>
<option value="monospace"> Monospace </option>
<option value="arial"> Arial </option>
<option value="courier"> Courier </option>
</select>
)
} src/components/SizeChanger.js
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update( parseInt(e.target.value) ) } disabled={ this.state.allowEdit === "false" }>
<option value="12"> 12 </option>
<option value="13"> 13 </option>
<option value="14"> 14 </option>
</select>
)
}At first glance it seems everything is working fine in our application, however when we change the value of allowEdit our child components are not updating with the new value. In this step, we will fix this bug using a react life cycle method called componentWillReceiveProps.
- Open
ColorChanger.js,FamilyChanger.js, andSizeChanger.jsfromsrc/components/. - Add a
componentWillReceivePropsmethod underneath theconstructormethod where it says// componentWillReceivePropsin all three files:- This method should take in a parameter called
props. - This method should use
setStateto update the value ofallowEditon state to the value ofallowEditonprops.
- This method should take in a parameter called
src/components/ColorChanger.js
import React, { Component } from 'react';
export default class ColorChanger extends Component {
constructor(props) {
super(props);
this.state = {
allowEdit: this.props.allowEdit
};
}
componentWillReceiveProps(props) {
this.setState({ allowEdit: props.allowEdit });
}
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update(e.target.value) } disabled={ this.state.allowEdit === "false" }>
<option value="black"> Black </option>
<option value="blue"> Blue </option>
<option value="green"> Green </option>
</select>
)
}
} src/components/FamilyChanger.js
import React, { Component } from 'react';
export default class FamilyChanger extends Component {
constructor(props) {
super(props);
this.state = {
allowEdit: this.props.allowEdit
};
}
componentWillReceiveProps(props) {
this.setState({ allowEdit: props.allowEdit });
}
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update(e.target.value) } disabled={ this.state.allowEdit === "false" }>
<option value="monospace"> Monospace </option>
<option value="arial"> Arial </option>
<option value="courier"> Courier </option>
</select>
)
}
} src/components/SizeChanger.js
import React, { Component } from 'react';
export default class SizeChanger extends Component {
constructor(props) {
super(props);
this.state = {
allowEdit: this.props.allowEdit
};
}
componentWillReceiveProps(props) {
this.setState({ allowEdit: props.allowEdit });
}
render() {
return (
<select className="dropDownContainer" onChange={ (e) => this.props.update( parseInt(e.target.value) ) } disabled={ this.state.allowEdit === "false" }>
<option value="12"> 12 </option>
<option value="13"> 13 </option>
<option value="14"> 14 </option>
</select>
)
}
}If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.





