I am struggling with the design of a React application which I am building. The app has a chat window on the left and the content of the application on the right. When a user enters a command, the backend understands the nature of the command (uses Luis.ai and Microsoft Bot Framework) which all works as expected. However the React piece is where I'm struggling.
Say the user enters a command which states they want to update a person by entering "Update name to Bill". The application correctly understands that the command is update the person and should load the edit version of my person overview.
However I'm not sure exactly the best approach on how to do this. The approach I'm taking now basically loads an OverviewWrapper component. Inside of the Overview wrapper component based on the props passed to it should load either the Edit or the View pane. The View pane is loaded by default.
I guess I'm not sure if I should be trying to load the edit component by changing the state or if I should be trying to use the navigate function. Thank you in advance for your guidance.
Here is my code.
export default class Patient extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.directLine = new DirectLine({
secret: "SOMEVALUEHERE"
});
this.state = {
PATIENT: [],
COMPPROPS: [],
};
this.setPanelState = this.setPanelState.bind(this);
}
//Set State of COMPPROPS so that compState should be edit
setPanelState(activity) {
var _compState = 'Edit';
var _compName = 'Overview';
this.setState({COMPPROPS: [{compName: 'Overview', compState: _compState}]});
return _compState;
}
componentWillMount() {
getPatient().then((result) => {
this.setState({PATIENT: result});
});
//review any chat activity and if the command is update patient then run setpanelstate method which should set the state
this.directLine.activity$
.filter(function (activity) {
return activity.type === 'event' && activity.value === 'Update Patient';
})
.subscribe((activity) => {
console.log("Im editing the overview");
var _compState2
_compState2 = this.setPanelState(activity);
console.log('CompStateVar:'+_compState2)
})
}
render() {
const OverviewWrapper = this.state.COMPPROPS.compState === 0 ? OverviewEditPane: OverviewPane
return (
...
<Box colorIndex='light-2' direction='row' flex={false}>
<div>
<OverviewWrapper overview={this.state.PATIENT} ovtype={this.state.COMPPROPS} />
</div>
</Box>
I resolved this by keeping the existing design by only setting one value compState in the SetState value rather then trying to set both compName and compState. Once I did that everything seemed to work fine.
Related
I have a series of buttons mapped from an API list. When pressed, the buttons change a users rank value. however I want it so that a user can not assign a lower rank than the one they currently have, so I have made it so that the buttons disable if the key value is lower than the current rank key value. the functionality works, however you only see the buttons become disabled if you refresh the page.
export default class rankChangeButtons extends React.Component{
constructor() {
super()
this.state = {
data: [],
rankId: this.props.rankId,
rank: this.props.rank,
}
this.updateRank = this.updateRank.bind(this);
}
getData = () => {
//this is where I call the API
data: res.data
}
updateRank(element) {
const { id, value } = element.target;
let headers: any = {"rankingList": this.props.rankingListId, "Content-Type" : "application/json"};
var payload = {"rankId":id,"affiliationId":this.props.affiliationId};
saveRankChange(payload, headers).then(()=>{
//call buttonAction function on parent component.
this.props.buttonAction(
id, value
)
//id and value are passed to parent to update rank
}).catch((e: any)=>{})
}
render() {
return(
<div>
<h5>User Rank:</h5>
<div className='buttonContainer'>
{this.state.data.map((rank =>
<li key={rank.key}>
<button id={rank.key} value = {rank.text} onClick={this.updateRank} disabled={rank.key <= this.state.rankId ? true : false}>
{rank.text}
</button>
</li>
))}
</div>
</div>
)
}
}
}
I would like to know if there is a way to see which buttons are disabled after changing the value without having to refresh the page.
Are you sure that this.updateRank is changing states?
Because according to react docs, if there's a state change, then the render method is called again with this.state updated.
You could see the button getting disabled if one of these options actually happened:
this.updateRank updates this.state.rankId.
this.updateRank calls a function that changes the property rank on the item you need on this.state.data.
If everything is fine, then show us this.updateRank code or how exactly the update happens.
So I'm quite new on web development last couple of days. I come from c++ background and I can't wrap my head through all the principles of reactjs. I have 2 classes. The child class called JobAd should render some information that it got from props.
export default class JobAd extends Component {
constructor(props) {
super(props);
this.state ={
index: props.index,
id: props.jobId,
name: props.name,
description: props.description,
location: props.location,
adress: props.adress,
alreadyApplied: props.alreadyApplied,
open: false,
// toggleJob: props.toggleJob,
};
this.toggleJob = props.toggleJob;
}
render() {
return (
<div className={`${styles.jobAd} d-flex` + "job " + (this.state.open ? 'open': '')} key={this.state.index} onClick={() => this.toggleJob(this.state.index)}>
<div className={`${styles.jobTitle}`}>
{this.state.location} - {this.state.name}
</div>
<div className={`${styles.jobDetails}`}>
<div className={`${styles.jobDescription}`}> {this.state.description}</div>
<div className={`${styles.jobAdress}`}>{this.state.adress}</div>
<ApplyButton jobId= {this.props.id} alreadyApplied = {this.props.alreadyApplied}/>
</div>
</div>
)
}
}
The second class, queries a mongoDB db and creates jobAd objects populating them from the info gotten from db.
class JobExplorer extends React.Component
{
...
result.data.jobs.forEach(job => {
var find = job.employees.find(obj => obj === userId);
if (!(find === undefined)) {
alreadyApplied = true;
}
var toPush = new JobAd ({
index: i,
id:job._id,
description:job.description,
name:job.name,
location:job.locationName,
adress:job.locationAdress,
alreadyApplied:alreadyApplied,
open:false,
toggleJob: this.toggleJob.bind(this)
});
jobList2.push(toPush);
console.log("look");
console.log(jobList2)
});
this.setState({
jobList: jobList2
})
this.setState({
error: null,
jobs: result.data.jobs
});
...
render()
{
console.log("look2");
console.log(this.state.jobList);
return (
<div><Navigation />
{this.state.jobList}
</div>
);
}
But I am faced with the following error which I cannot find a fix for.
Error: Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state, toggleJob}). If you meant to render a collection of children, use an array instead.
How should I instantiate those objects so I could render them using the "architecture" I wrote. Is there a fundamental flaw that I have in my classes?
The below snippet doesn't work because new will return an object (this) not the react component.
So, instead of
var toPush = new JobAd({
index: i,
id: job._id,
...
});
jobList2.push(toPush);
you can do this
var toPush = <JobAd
index={i}
id={job._id}
...
/>;
The above snippet works because <JobAd ... /> is converted to React.createElement(JobAd, ... ). However, you still shouldn't do it like this. since there are a lot of better ways to do this. one of them is:
save just the data in joblist and then render the data list on JobAd component
like below:-
render(){
return this.state.joblist.map((job, i) => (
<JobAd
key={job._id}
index={i}
...
/>
));
}
The key is a really important thing. Read about it: https://reactjs.org/docs/lists-and-keys.html
Things that could be improved:-
Don't copy props in the state as you are doing in JobAd class instead directly render the props.
Don't call setState twice as in JobExplorer. you could set all the keys in
setState at the same time. since that would render the component twice.
Suggestions:-
You should avoid using var as that might cause some issues here.
since, you are just a starter, try using functional component first. they are
quite easier to grasp
You seem to have a misconception about state/props in React and web development. It's very normal; I learned python and Java first and many tutorials seem to assume that people just know this already.
"State" in generally refers to variables containing/referring to values that can change without a page refresh in your application. If you know a value is not going to change, it does not need to be held in state. Storing it in a normal variable is exactly what you should do.
"Props" is just another word for arguments that are passed to React components. There's more to it in reality, but as a beginner, that's all you need to really know for now.
So in your job add, things like name, address, jobs, description shouldn't go in state because they aren't going to change as a result of user interaction or for any other reason, unless the underlying data they are loaded from changes, but then that wouldn't be handled by React but instead by the API that your app gets data from. They should just be rendered, so refer to them like this.props.address in your render method. The value for open, however, need to be in state, because that definitely can change.
As for the error, it looks like you are not calling JobAd correctly. You need to use the syntax <Job Ad/> rather than new JobAd...that won't work in React.
I would recommend doing a tutorial to get the basics down.
I am new to react programming, and I have no idea how to set "incoming data" from a hyperlink on a web page when a user clicks a link an email.
In other words, I get an email with a link in it... I click on the link, that link takes me to a web page and populates two edit boxes with data (from the link).
I know how to code up the back end api that would accept data in api formate, for example in the 'discounts.js' api file, I could do this:
router.post('/discounts/:userCode/discount/:discountCode'
BUT what I want to do is send this link via email, have the user click the link and have the resulting page populated with data.
Here is an example link
example.com/discountPage/:snvpsnan8zosfluawpcn50ycdipa6rrqlym/code/:sdfiedgmmkfzuky2f6grds
So after the user clicks the link, the REACT web page edit boxes should look like this:
given the following react (partial) component:
class discountPage extends Component {
constructor() {
super();
this.state = {
userCode: '',
discountCode: ''
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
I am thinking I might be able to set the data in the componentDidMount - but really unsure of where the data from link comes from and how it gets set?
Does it come from the props ?
componentDidMount() {
console.log('Register : componentDidMount ');
this.setState({'userCode': this.props.userCode });
this.setState({'discountCode': this.props.discountCode });
}
UPDATE
Based on Pinaka30's response... I did some searching and found this:
https://www.webdeveloperpal.com/2018/02/21/react-router-get-url-params/
import queryString from 'query-string';
class UserComponent extends React.Component{
render(){
// url is 'https://www.example.com/user?id=123&type=4';
let url = this.props.location.search;
let params = queryString.parse(url);
console.log(params);
// The result will be like below
// { id: 123, type: 4 }
// other code
}
}
There can be many methods, and the one I am listing might not be the best, but it will definitely give you desired results.
Since you are clicking a link and then being redirected to a new page with your form, the request will be a GET request. As we know, the parameters are visible in the url in case of a GET request. So what you can do is retrieve userCode and DiscountCode from the url itself.
componentDidMount(){
var url = window.location.href; //Gives you the complete url
temp = url.split(""); //Split the url to retrieve both parameters according to your format
this.setState({
userCode: temp[0],
discountCode: temp[1]
});
}
In your render method, use state as the value
<input id="userCode" value={this.state.userCode} />
<input id="discountCode" value={this.state.discountCode} />
So as soon as your page loads and the component renders, componentDidMount will get called and update the states which will be reflected in your input boxes. Just split the url carefully. I didn't write that line completely because i didn't know the format.
I am building on sample 16 from Github/Webchat to build a webpage with a webchat interface.
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/16.customization-selectable-activity
The react app consists off different .js files to build the website( Webchat.js, Instuctor.js, App.js, Index.js) and I can't provide file Inspector.js with the data I gathered in file Webchat.js.
I can't find the right code to read the value of a variable from file Webchat.js in file Inspector.js.
I want to build a Webpage where I have on the left side a Chatbot (BotFrameWork) running, and next to it a table running which shows data that has been collected by the chatbot.
I tried answers from
how to get a variable from a file to another file in node.js
but doesn't work.
I tried to get the state of Webchat but gives only undefined.
Example:
(webchat.js) I fetched data from the bot (like [link]How to create a side window or iframe that shows data that was gathered by a Web-Chat in one web page?) and saved it in a state variable 'test'.
(instructor.js) I want to show that data e.g. in a label that is getting updated when new data comes in. How do I get access now to the value of 'test' that is created in another file?
What doesnt work:
in instuctor.js:
var test2 = require ('./webchat');
Console.log(test2.state.test) //this is how I imagine it to work --> undefined
with require I only get an object that has a 'name' variable 'Webchat' and which i can get out with: console.log(test2.default.name);
React only supports one-way data binding, so if you want to share a variable between multiple components, you need to elevate the state to the parent and pass the variable and change handlers to the children as props.
In the example below, Parent has two children: ChildA and ChildB. We could keep myValue in ChildA's state, but then ChildB wouldn't be able to access it, so we elevate myValue to the parent and pass it to both children as props. We also, pass a change handler to ChildB so it can update the value when the user clicks it.
import React from 'react';
const ChildA = ({ myValue }) => (
<div>
{
myValue
? <h1>Hello World</h1>
: <h1>Goodbye!</h1>
}
</div>
);
const ChildB = ({ myValue, handleMyValueChange}) => (
<button onClick={ () => handleMyValueChange(false) } disabled={ myValue }>
Click Me!
</button>
);
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { myValue: true }
}
render() {
return (
<div>
<ChildA myValue={this.props.myValue}/>
<ChildB myValue={this.props.myValue} handleMyValueChange={ handleMyValueChange }/>
</div>
)
}
handleMyValueChange = myValue => {
this.setState({ myValue });
}
}
In terms of the sample you referenced, the parent class is App and the two children are ReactWebChat and Inspector. I would recommend elevating the state of your variable to the parent - App - and pass it as a prop to the Inspector class. Then you can add a custom store middleware to ReactWebChat that updates your variable when the bot sends an update event. For more information on how to configure your bot to send update events and how to make Web Chat listen for them, take a look at this StackOverflow Question.
Hope this helps!
I have an issue with ReactJS. Specifically componentWillUnmount is not being called across the project at all (a semi-large corporate website). I will list the steps I have taken to diagnose and pinpoint the issue:
I am using a simple console.log message to test, to exclude problems inside the function
I have placed it in different jsx files in the project, to exclude function positioning problems
I have checked against switching pages in the app and loading a different website altogether
I checked to see whether the function is called from parents, siblings or children, since I've found competing lifecycle calls can cause neither to work, but it's the only one in the project so far
I tried it on a different branch with no effect
I tried it on a colleague's computer with no effect
A different lifecycle function (componentWillMount) works fine
I am using ES6 with react 15.1.0, react-bootstrap and react-validation. The relevant code is below, but keep in mind I have placed the function in numerous places and it does not appear to get called anywhere.
export default class YourData extends React.Component {
constructor(props){
super(props);
this.getMainBookers = this.getMainBookers.bind(this);
this.bookingTravelCompanyForms = this.props.your_data.bookings.map(
(booking, i) => {
var mainBookers = i > 0 ? this.getMainBookers : false;
return (
<BookingTravelCompanyForm booking={booking} key={i}
mainBookers={mainBookers}
onInputChange={this.props.onInputChange}/>
);
}
);
}
componentWillMount(){
this.props.initializeInput();
}
componentWillUnmount(){
console.log('willunmount yourdata');
this.props.saveInput();
}
getMainBookers() {
var mainform = this.bookingTravelCompanyForms[0];
return mainform.props.booking.company;
}
handleSubmit(event) {
event.preventDefault();
// Emulate async API call
setTimeout(() => {
this.refs.form.showError('username', <span onClick={this.removeApiError.bind(this)}
className='form-error is-visible'>API Error. Click to hide out.</span>);
}, 1000);
}
copyTravelCompany() {
var travelCompany = this.bookingTravelCompanyForms[0];
this.setState({
travelCompany: travelCompany
});
}
render() {