-
Notifications
You must be signed in to change notification settings - Fork 39
refactor: changed Output into a functional component w/ hooks #686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
25d8cc3
f4e0391
ac29fe6
0b85734
235a13b
a07f756
4b6b962
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,156 +1,143 @@ | ||
| import { faPlay, faTerminal } from '@fortawesome/free-solid-svg-icons'; | ||
|
|
||
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||
| import React from 'react'; | ||
| import React, { useState, useEffect } from 'react'; | ||
| import { Button } from 'reactstrap'; | ||
| import { OUTPUT_ONLY } from '../../constants'; | ||
| import OpenPanelButtonContainer from '../common/containers/OpenPanelButtonContainer.js'; | ||
| import ViewportAwareButton from '../common/ViewportAwareButton.js'; | ||
| import EditorRadio from '../TextEditor/components/EditorRadio.js'; | ||
| import OpenPanelButtonContainer from '../common/containers/OpenPanelButtonContainer'; | ||
| import ViewportAwareButton from '../common/ViewportAwareButton'; | ||
| import EditorRadio from '../TextEditor/components/EditorRadio'; | ||
|
|
||
| /** --------Props-------- | ||
| * None | ||
| */ | ||
|
|
||
| class Output extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
| this.state = { | ||
| // used for the refresh button | ||
| counter: 0, | ||
| run: 0, | ||
| showConsole: true, | ||
| }; | ||
| this.firstLoad = true; | ||
| const compareProps = (prevProps, nextProps) => { | ||
| if (prevProps.mostRecentProgram !== nextProps.mostRecentProgram) { | ||
| return true; | ||
| } | ||
|
|
||
| //= =============React Lifecycle Functions===================// | ||
| shouldComponentUpdate = (nextProps, nextState) => { | ||
| if (this.state.showConsole !== nextState.showConsole) { | ||
| return true; | ||
| } | ||
|
|
||
| if (this.props.mostRecentProgram !== nextProps.mostRecentProgram) { | ||
| this.firstLoad = true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This functionality is not preserved
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return true; | ||
| } | ||
|
|
||
| if (this.props.isSmall !== nextProps.isSmall) { | ||
| return true; | ||
| } | ||
|
|
||
| if ( | ||
| this.state.run !== nextState.run | ||
| || this.state.counter !== nextState.counter | ||
| || this.state.showConsole !== nextState.showConsole | ||
| ) { | ||
| this.firstLoad = false; | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
|
|
||
| renderOpenPanelButton = () => this.props.viewMode === OUTPUT_ONLY && <OpenPanelButtonContainer />; | ||
|
|
||
| renderIframe = (getSrcDoc) => { | ||
| // check if getsrcdoc is a function | ||
| if (!getSrcDoc && {}.toString.call(getSrcDoc) === '[object Function]') { | ||
| console.log('Null src doc function found'); | ||
| return null; | ||
| } | ||
| if (prevProps.isSmall !== nextProps.isSmall) { | ||
| return true; | ||
| } | ||
|
|
||
| return ( | ||
| <iframe | ||
| id={`${this.state.counter} ${this.state.run}`} | ||
| key={`${this.state.counter} ${this.state.run}`} | ||
| className="editor-output-iframe" | ||
| style={{ height: `${this.props.screenHeight - 61}px` }} | ||
| srcDoc={getSrcDoc()} | ||
| src="" | ||
| title="output-iframe" | ||
| onLoad={() => {}} | ||
| /> | ||
| return false; | ||
| }; | ||
|
|
||
| const Output = React.memo( | ||
| ({ | ||
| viewMode, | ||
| viewOnly, | ||
| screenHeight, | ||
| vLanguage, | ||
| language, | ||
| code, | ||
| runResult, | ||
| updateViewMode, | ||
| isSmall, | ||
| }) => { | ||
| const counter = 0; | ||
| const [run, setRun] = useState(0); | ||
| const [showConsole, setShowConsole] = useState(true); | ||
| const [firstLoad, setFirstLoad] = useState(true); | ||
|
|
||
| useEffect(() => { | ||
| setFirstLoad(false); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the |
||
| }, [run, counter, showConsole]); | ||
|
|
||
| const renderOpenPanelButton = () => viewMode === OUTPUT_ONLY && <OpenPanelButtonContainer />; | ||
|
|
||
| const renderIframe = (getSrcDoc) => { | ||
| // check if getsrcdoc is a function | ||
| if (!getSrcDoc && {}.toString.call(getSrcDoc) === '[object Function]') { | ||
| // console.log('Null src doc function found'); | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <iframe | ||
| id={`${counter} ${run}`} | ||
| key={`${counter} ${run}`} | ||
| className="editor-output-iframe" | ||
| style={{ height: `${screenHeight - 61}px` }} | ||
| srcDoc={getSrcDoc()} | ||
| src="" | ||
| title="output-iframe" | ||
| onLoad={() => {}} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| const renderOutput = () => { | ||
| const lang = viewOnly ? vLanguage : language; | ||
| const runRes = viewOnly ? code : runResult; | ||
|
|
||
| if (firstLoad) { | ||
| return null; | ||
| } | ||
|
|
||
| // if there's nothing to run, don't render an output | ||
| if (!runRes || !runRes.length) { | ||
| return null; | ||
| } | ||
|
|
||
| const srcDocFunc = () => lang.render(runRes, showConsole); | ||
| return renderIframe(srcDocFunc); | ||
| }; | ||
|
|
||
| const renderRadio = () => viewMode === OUTPUT_ONLY && ( | ||
| <div style={{ marginLeft: 'auto' }}> | ||
| <EditorRadio viewMode={viewMode} updateViewMode={updateViewMode} isSmall={isSmall} /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| renderOutput = () => { | ||
| const language = this.props.viewOnly ? this.props.vLanguage : this.props.language; | ||
| const runResult = this.props.viewOnly ? this.props.code : this.props.runResult; | ||
| const { showConsole } = this.state; | ||
|
|
||
| if (this.firstLoad) { | ||
| return null; | ||
| } | ||
|
|
||
| // if there's nothing to run, don't render an output | ||
| if (!runResult || !runResult.length) { | ||
| return null; | ||
| } | ||
|
|
||
| const srcDocFunc = () => language.render(runResult, showConsole); | ||
| return this.renderIframe(srcDocFunc); | ||
| }; | ||
|
|
||
| renderRadio = () => this.props.viewMode === OUTPUT_ONLY && ( | ||
| <div style={{ marginLeft: 'auto' }}> | ||
| <EditorRadio | ||
| viewMode={this.props.viewMode} | ||
| updateViewMode={this.props.updateViewMode} | ||
| isSmall={this.props.isSmall} | ||
| /> | ||
| </div> | ||
| ); | ||
|
|
||
| toggleConsole = () => { | ||
| this.setState((prevState) => ({ showConsole: !prevState.showConsole })); | ||
| }; | ||
|
|
||
| renderConsoleButton = () => ( | ||
| <Button | ||
| className="mx-2" | ||
| color={this.state.showConsole ? 'danger' : 'primary'} | ||
| onClick={this.toggleConsole} | ||
| title={this.state.showConsole ? 'Hide Console' : 'Show Console'} | ||
| size="lg" | ||
| > | ||
| <FontAwesomeIcon icon={faTerminal} /> | ||
| </Button> | ||
| ); | ||
|
|
||
| renderBanner = () => ( | ||
| <div className="editor-output-banner"> | ||
| {this.renderOpenPanelButton()} | ||
| <div style={{ flex: '1 1 auto' }}> </div> | ||
| {' '} | ||
| {/* whitespace */} | ||
| {this.renderRadio()} | ||
| {this.renderConsoleButton()} | ||
| <ViewportAwareButton | ||
|
|
||
| const toggleConsole = () => { | ||
| setShowConsole(!showConsole); | ||
| }; | ||
|
|
||
| const renderConsoleButton = () => ( | ||
| <Button | ||
| className="mx-2" | ||
| color="primary" | ||
| color={showConsole ? 'danger' : 'primary'} | ||
| onClick={toggleConsole} | ||
| title={showConsole ? 'Hide Console' : 'Show Console'} | ||
| size="lg" | ||
| onClick={this.runCode} | ||
| isSmall={this.props.isSmall} | ||
| icon={<FontAwesomeIcon icon={faPlay} />} | ||
| text="Run" | ||
| /> | ||
| </div> | ||
| ); | ||
|
|
||
| runCode = () => { | ||
| this.setState((prevState) => ({ | ||
| run: prevState.run + 1, | ||
| })); | ||
| }; | ||
|
|
||
| render() { | ||
| > | ||
| <FontAwesomeIcon icon={faTerminal} /> | ||
| </Button> | ||
| ); | ||
|
|
||
| const runCode = () => { | ||
| setRun((prevRun) => prevRun + 1); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should just be the run state, instead of this function |
||
| }; | ||
|
|
||
| const renderBanner = () => ( | ||
| <div className="editor-output-banner"> | ||
| {renderOpenPanelButton()} | ||
| <div style={{ flex: '1 1 auto' }}> </div> | ||
| {/* whitespace */} | ||
| {renderRadio()} | ||
| {renderConsoleButton()} | ||
| <ViewportAwareButton | ||
| className="mx-2" | ||
| color="primary" | ||
| size="lg" | ||
| onClick={runCode} | ||
| isSmall={isSmall} | ||
| icon={<FontAwesomeIcon icon={faPlay} />} | ||
| text="Run" | ||
| /> | ||
| </div> | ||
| ); | ||
|
|
||
| return ( | ||
| <div className="editor-output"> | ||
| {this.renderBanner()} | ||
| <div>{this.renderOutput()}</div> | ||
| {renderBanner()} | ||
| <div>{renderOutput()}</div> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
| }, | ||
| compareProps, | ||
| ); | ||
|
|
||
| export default Output; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trueshould be returned when the component should not update; you reversed it