How can I handle pending state?
Opened this issue · 1 comments
superbogy commented
It seems that inputAccept
doesn't not supported get command result in async way.
For example: I send a request then wait for response and render result. This is a async action, component state should be set to pending before response and rerender output result after response.But inputAccept
response directly in a sync way.
storm1er commented
Here's how I do:
File App.js
import React, { Component } from "react";
import { ReactTerminalStateless } from "react-terminal-component";
import { EmulatorState, CommandMapping, Emulator } from "javascript-terminal";
import all from "./commands/all";
import TerminalContext, {
defaultTerminalContext
} from "./contexts/TerminalContext";
class App extends Component {
constructor() {
super();
const getTerminalState = defaultTerminalContext.getTerminalState.bind(this);
const setTerminalState = defaultTerminalContext.setTerminalState.bind(this);
const setAcceptInput = defaultTerminalContext.setAcceptInput.bind(this);
const terminalState = EmulatorState.createEmpty().setCommandMapping(
CommandMapping.create(
all(getTerminalState, setTerminalState, setAcceptInput)
)
);
this.state = {
...defaultTerminalContext,
getTerminalState,
setTerminalState,
setAcceptInput,
terminalState
};
}
triggerAction() {
const emulator = new Emulator();
const newTerminalState = emulator.execute(
this.state.terminalState, "print i clicked the btn", []
);
this.setState((prevState) => {
this.setState({
...prevState,
terminalState: newTerminalState,
});
})
}
render() {
return (
<TerminalContext.Provider value={this.state}>
<ReactTerminalStateless
acceptInput={this.state.acceptInput}
emulatorState={this.state.terminalState}
inputStr={this.state.inputStr}
onInputChange={inputStr => this.setState({ inputStr })}
onStateChange={terminalState =>
this.setState({ terminalState, inputStr: "" })
}
clickToFocus={true}
theme={{
background: "#141313",
promptSymbolColor: "#6effe6",
commandColor: "#fcfcfc",
outputColor: "#fcfcfc",
errorOutputColor: "#ff89bd",
fontSize: "1.1rem",
spacing: "1%",
fontFamily: "monospace",
width: "100%",
height: "50vh"
}}
/>
<span onClick={this.triggerAction.bind(this)}>test action on click</span>
</TerminalContext.Provider>
);
}
}
export default App;
File src\contexts\TerminalContext.tsx
import React from "react";
import App from "../App";
function isAppComponent(el: any): el is App {
if (typeof el === "undefined" || typeof el.setState !== "function") {
return false;
}
return true;
}
const defaultTerminalContext = {
inputStr: "",
terminalState: null,
acceptInput: true,
setAcceptInput: function(acceptInput: boolean): void {
if (!isAppComponent(this)) {
throw new Error(
"You must bind this function to the App component to use it."
);
}
this.setState((prevState: any) => ({ ...prevState, acceptInput }));
},
setTerminalState: function(newState: any): void {
if (!isAppComponent(this)) {
throw new Error(
"You must bind this function to the App component to use it."
);
}
this.setState((prevState: any) => ({ ...prevState, terminalState: newState }));
},
getTerminalState: function(): any {
if (!isAppComponent(this)) {
throw new Error(
"You must bind this function to the App component to use it."
);
}
return this?.state?.terminalState;
}
};
const TerminalContext = React.createContext(defaultTerminalContext);
TerminalContext.displayName = "TerminalContext";
export { defaultTerminalContext };
export default TerminalContext;
File src\commands\print.js
import { OutputFactory, Outputs } from 'javascript-terminal';
const print = (getTerminalStateFn, setTerminalStateFn, setAcceptInput) => {
return {
'print': {
'function': (state, opts) => {
setAcceptInput(false);
const input = opts.join(' ');
setTimeout(()=>{
const oldState = getTerminalStateFn();
const oldOutputs = oldState.getOutputs();
const newOutputs = Outputs.addRecord(
oldOutputs, OutputFactory.makeTextOutput(input)
);
const newState = oldState.setOutputs(newOutputs);
setAcceptInput(true);
setTerminalStateFn(newState);
}, 1000);
return {
output: OutputFactory.makeTextOutput("waiting ..."),
};
},
'optDef': {}
}
}
};
export default print;
File src\commands\all.js
import print from './print';
const createMapping = (getTerminalStateFn, setTerminalStateFn, setAcceptInput) => {
return {
...print(getTerminalStateFn, setTerminalStateFn, setAcceptInput),
}
}
export default createMapping;