How to fix scope problem inside "then" function react? - javascript

I am trying to print out of Printer. I am a little new to react and javascript. I am trying to pass the state to a then function of Third Party Code. But i am getting an error:
Cannot read property 'restaurant_name' of undefined
How can i pass state to the scope of then function of qz?
print = () => {
let { state } = this.state;
qz.websocket.connect()
.then(function() {
return qz.printers.find("BillPrinter");
}).then(function(printer) {
var config = qz.configs.create(printer);
var data = [
`${state.restaurant_name}` + '\x0A',
`${state.restaurant_address}`
]
return qz.print(config, data);
});
}

You have some unnecessary destructuring that is causing your error - this.state.state doesn't exist, yet this line:
let { state } = this.state;
Is equivalent to:
let state = this.state.state;
Remove the curly braces and it'll work fine.
let state = this.state;
Also note that state will be a reference to this.state rather than being another object.

Use arrow function to keep the function in the upper scope as #Ali Torki suggested:
.then(printer => {....})

Related

How do i return an object from my react state

I am trying to find an item from a collection, from the code below, in order to update my react component, the propertState object isnt empty, it contains a list which i have console logged, however I seem to get an underfined object when i console log the value returned from my findProperty function... I am trying update my localState with that value so that my component can render the right data.
const PropertyComponent = () => {
const { propertyId } = useParams();
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
const[property, setProperty] = useState()
const findProperty = (propertyId, properties) => {
let propertyReturn;
for (var i=0; i < properties.length; i++) {
if (properties[i].propertyId === propertyId) {
propertyToReturn = properties[i];
break;
}
}
setProperty(propertyReturn)
return propertyReturn;
}
const foundProperty = findProperty(propertyId, propertyState.properties);
return (<>{property.propertyName}</>)
}
export default PropertyComponent
There are a few things that you shall consider when you are finding data and updating states based on external sources of data --useParams--
I will try to explain the solution by dividing your code in small pieces
const PropertyComponent = () => {
const { propertyId } = useParams();
Piece A: Consider that useParams is a hook connected to the router, that means that you component might be reactive and will change every time that a param changes in the URL. Your param might be undefined or an string depending if the param is present in your URL
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
Piece B: useSelector is other property that will make your component reactive to changes related to that selector. Your selector might return undefined or something based on your selection logic.
const[property, setProperty] = useState()
Piece C: Your state that starts as undefined in the first render.
So far we have just discovered 3 pieces of code that might start as undefined or not.
const findProperty = (propertyId, properties) => {
let propertyReturn;
for (var i=0; i < properties.length; i++) {
if (properties[i].propertyId === propertyId) {
propertyToReturn = properties[i];
break;
}
}
setProperty(propertyReturn)
return propertyReturn;
}
const foundProperty = findProperty(propertyId, propertyState.properties);
Piece D: Here is where more problems start appearing, you are telling your code that in every render a function findProperty will be created and inside of it you are calling the setter of your state --setProperty--, generating an internal dependency.
I would suggest to think about the actions that you want to do in simple steps and then you can understand where each piece of code belongs to where.
Let's subdivide this last piece of code --Piece D-- but in steps, you want to:
Find something.
The find should happen if you have an array where to find and a property.
With the result I want to notify my component that something was found.
Step 1 and 2 can happen in a function defined outside of your component:
const findProperty = (propertyId, properties) => properties.find((property) => property.propertyId === propertyId)
NOTE: I took the liberty of modify your code by simplifying a little
bit your find function.
Now we need to do the most important step, make your component react at the right time
const findProperty = (propertyId, properties) => properties.find((property) => property.propertyId === propertyId)
const PropertyComponent = () => {
const { propertyId } = useParams();
const propertyState: IPropertiesState = useSelector(
propertiesStateSelector
);
const[property, setProperty] = useState({ propertyName: '' }); // I suggest to add default values to have more predictable returns in your component
/**
* Here is where the magic begins and we try to mix all of our values in a consistent way (thinking on the previous pieces and the potential "undefined" values) We need to tell react "do something when the data is ready", for that reason we will use an effect
*/
useEffect(() => {
// This effect will run every time that the dependencies --second argument-- changes, then you react afterwards.
if(propertyId, propertyState.properties) {
const propertyFound = findProperty(propertyId, propertyState.properties);
if(propertyFound){ // Only if we have a result we will update our state.
setProperty(propertyFound);
}
}
}, [propertyId, propertyState.properties])
return (<>{property.propertyName}</>)
}
export default PropertyComponent
I think that in this way your intention might be more direct, but for sure there are other ways to do this. Depending of your intentions your code should be different, for instance I have a question:
What is it the purpose of this component? If its just for getting the property you could do a derived state, a little bit more complex selector. E.G.
function propertySelectorById(id) {
return function(store) {
const allProperties = propertiesStateSelector(store);
const foundProperty = findProperty(id, allProperties);
if( foundProperty ) {
return foundProperty;
} else {
return null; // Or empty object, up to you
}
}
}
Then you can use it in any component that uses the useParam, or just create a simple hook. E.G.
function usePropertySelectorHook() {
const { propertyId } = useParams();
const property = useSelector(propertySelectorById(propertyId));
return property;
}
And afterwards you can use this in any component
functon AnyComponent() {
const property = usePropertySelectorHook();
return <div> Magic {property}</div>
}
NOTE: I didn't test all the code, I wrote it directly in the comment but I think that should work.
Like this I think that there are even more ways to solve this, but its enough for now, hope that this helped you.
do you try this:
const found = propertyState.properties.find(element => element.propertyId === propertyId);
setProperty(found);
instead of all function findProperty

React Function Component use variable (not state)

With React Class Component, I use some variable (not this.state) helping my control logic. Example: this.isPressBackspace = false and when I set variable don't make component re-render (ex: this.isPressBackspace = true).
That's working perfect in Class Component but when I change to Function Component, I dont know where to place this.isPressBackspace.
Here is my example in codesandbox.
https://codesandbox.io/s/function-component-example-3h98d
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
const isPressBackspaceRef = React.useRef(false);
const keyDownPositionRef = React.useRef({});
const onKeyDown = (e) => {
// this is wrong syntax
// this.keyDownPosition OR let keyDownPosition
keyDownPositionRef.current = {
start: e.target.selectionStart,
end: e.target.selectionEnd
};
switch (e.key) {
case "Backspace":
isPressBackspaceRef.current = true; // this is wrong syntax ????
break;
default:
break;
}
};
const onChange = (e) => {
const { end } = keyDownPositionRef;
if (isPressBackspaceRef.current) {
const length = end - e.target.selectionEnd;
alert(`You delete ${length} character`);
}
isPressBackspaceRef.current = false;
};
In my experience you don't use the this keyword when working with function components. Instead you use hooks like useState.
Check the following video for getting started with hooks:
https://www.youtube.com/watch?v=O6P86uwfdR0&ab_channel=WebDevSimplified

Update state with setState ReactJs

I'm trying to update state variable when button click.but my issue is,it's update once with correct data then again it updated with constructor defined data.
constructor(props) {
super(props);
this.state = {
popupshow: [{ check: false, id: '' }]
}
}
componentDidUpdate(prevProps, prevState) {
console.log("this.state.popupshow",this.state.popupshow)
}
Details(type){
this.state.popupshow[i].id = type
this.state.popupshow[i].check = true;
this.setState({ popupshow: this.state.popupshow });
}
render() {
return (
<a onClick={() => this.Details("Tv Series")}>Update </>
)
}
my console.log is like bellow
You should not update React state directly. You should always update/set React state via setState method.
These lines are against React principal
this.state.popupshow[i].id = type
this.state.popupshow[i].check = true;
Update your Details as follows
Details(type){
let { popupshow } = this.state;
let i = 0;
popupshow[i].id = type
popupshow[i].check = true;
this.setState({ popupshow });
}
Note I dont have idea of variable i so assumed that as 0
I think you should rewrite details functions like :
Details(type, i){
const popupDetail = Object.assign([], this.state.popupshow);
popupDetail[i].id = type
popupDetail[i].check = true;
this.setState({ popupshow: popupDetail });
}
you are setting popupshow: this.state.popupshow this is causing forceupdate which re renders the component hence its value gets reset.
I totally agree with the other answers have given for the question, however there are few things worth noting is you might wanna add the function to the context.
The argument in favour of adding these lines to the constructor is so that the new bound functions are only created once per instance of the class. You could also use
onClick={this.Details.bind(this, "Tv Series")}
or (ES6):
onClick={() => this.Details("Tv Series")}
but either of these methods will create a new function every time the component is re-rendered.
Then change the function to arrow fucntion too like
Details(type, i){
const popupDetail = Object.assign([], this.state.popupshow);
popupDetail[i].id = type
popupDetail[i].check = true;
this.setState({ popupshow: popupDetail });
}

How to pass by value and not by reference in React?

I have the following file, LookupPage.jsx and AccountDetails.jsx.
In LookUp
this.updateCustomer = (customer) => {
if(JSON.stringify(customer.address) !== JSON.stringify(this.state.activeAccount.customer.address)) {
console.log('address changed');
customer.update_address = true;
customer.address.source = 'user';
}
return fetch(
`${API_ENDPOINT}/customer/${customer.id}/`,
{
method: 'PATCH',
headers: {
'Authorization': 'Token ' + this.props.session_token,
'Content-Type': 'application/json',
},
body: JSON.stringify(customer),
}
).then(restJSONResponseToPromise).then(responseJSON => {
if(responseJSON.results){
console.log('update customers client side.')
}
}, clearSessionIfInvalidToken(this.props.clearSession));
};
<AccountsDetailModal
show={this.state.showAccountDetail}
close={this.toggleAccountDetail}
customer={this.state.activeAccount.customer}
updateCustomer={this.updateCustomer}
/>
In side AccountDetails
this.onChangeAddress = (e) => {
const customer = {...this.state.customer};
const address = customer.address;
address[e.target.name] = e.target.value;
customer.address = address;
this.setState({customer, errors: {
...this.state.errors,
[e.target.name]: [],
}});
};
this.saveCustomer = () => {
this.setState({postDisable: true});
const errors = this.getFormErrors();
const hasErrors = !every(errors, (item) => !item.length);
if(!hasErrors){
this.props.updateCustomer(this.state.customer);
} else {
sweetAlert('Error!', 'Form is invalid.', 'error');
}
this.setState({postDisable: false});
};
this.componentDidMount = () => {
this.setState({customer: this.props.customer});
}
When I am updating the customers address, it is updating active accounts address, so it seems like it is being passed by reference. What I want to happen is only update the customer address if the address was changed/different from the original. How would I modify my code to do this?
You can pass any object by value in JS (whether you're using React or not) by passing:
JSON.parse(JSON.stringify(myObject))
as an argument instead of the object itself.
Essentially this will just clone the object and pass a copy of it, so you can manipulate the copy all you want without affecting the original.
Note that this will not work if the object contains functions, it will only copy the properties. (In your example this should be fine.)
I am going to put my two cents here:
First of all, this isn't really specific to React and is more of a JS related question.
Secondly, setting props against internal state is considered to be a bad practice when it comes to react. There's really no need to do that given your particular scenario. I am referring to
this.setState({customer: this.props.customer});
So, coming to your problem, the reason you are having reference issues is because you are mutating the original passed in object at certain points in your code. For instance, if I look at:
this.updateCustomer = (customer) => {
if(JSON.stringify(customer.address) !== JSON.stringify(this.state.activeAccount.customer.address)) {
console.log('address changed');
customer.update_address = true;
customer.address.source = 'user';
}
};
You are mutating the original props of the argument object which is very likely to be passed around in other methods of your component. So, to overcome that you can do:
const updatedCustomer = Object.assign({}, customer, {
update_address: true
});
And you can pass in updatedCustomer in your API call. Object.assign() will not perform operation on the passed in object but will return a new object so you can be sure that at any point in your app you are not mutating the original object.
Note: Object.assign would work on plain object and not a nested one. So, if you want to achieve something similar that would work on nested object properties too, you can use lodash merge.

cannot modify nodejs module value

I want initialize a module with some default values and change them later if required. To do this I have a module/singleton which contains a _state value. I have exposed a setter method to update that value. However, the setter does not update the _state member variable. The code looks like this:
var StateObject = function () {
var _state = { a: 1 };
return {
state : _state,
setState : function (s) {
_state = s;
}
};
}();
modules.export = StateObject;
and the calling function:
var SO = require('./state-object');
console.log(SO.state.a); // prints 1
SO.setState({a: 2});
console.log(SO.state.a); // still prints 1
Can anyone explain what would cause this and if there is a work around?
The potential pitfall of a solution like this is if some piece of code stores SO.state locally and references that. If that happens and you call setState() some time later, the reference won't be updated in that piece of code. That's just something to be aware of when you replace the whole state and not just individual values in the state.
The problem here has to do with references. When you execute StateObject(), the state variable stores the initial reference to _state. Then when you call setState(), you overwrite _state, but state is still holding on to the previous reference.
You might try something like this:
modules.export = {
state: { a: 1 },
setState: function(v) {
this.state = v;
}
};
Well, the problem is with the reference not being updated, as mscdex mentioned.
In my opinion the main problem is actually your logic: why have a setter if you don't have a getter?
var SO = function () {
var _state = { a: 1 };
return {
getState : function () {
return _state;
},
setState : function (s) {
_state = s;
}
};
}();
console.log(SO.getState().a); // prints 1
SO.setState({a: 2});
console.log(SO.getState().a); // prints 2
This works, as it is also returning the reference to the latest set object. And this has actually nothing to do with node or modules, you can run your example in the browser's JavaScript console.

Categories