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 });
}
Related
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
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
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 => {....})
I'm not quite sure where to initiate calculated values in a react component. So this is how I am doing it right now:
render () {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo
return (
<div>{calculatedValue}</div>
)
}
Or should I initiate calculatedValue in componentDidMount()
I think it really depends on how much calculation you are doing. For something as simple as your example above I would usually just do that in render().
Anything above very basic functionality and I would split it out into a separate function such as getFooBarCalcValue().
This way your render method doesn't get too cluttered with things that would otherwise be elsewhere.
This depends on how your calculated value is going to change in your application. If is a value that will not change, your just wanna when the first render of the component (what you probably not), you're fine calculating in componentDidMount and assigning to a property (this.calculatedValue = ...) and accessing in render with this.calculatedValue, but it's not the "React way".
Assuming you're using the state, something that will be change in your application, then you will need to put your calculation in somewhere else.
Here are some options to you to keep your calculated values:
1. In your render method, just like you did:
Fine for simple calculations, but believe me, this thinks can grow...
render () {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo
return (
<div>{calculatedValue}</div>
)
}
2. In a getter method:
keeping the calculation separated for your render method
getCalculatedValue() {
const { foo } = this.props
const { bar } = this.state
return bar ? foo * bar : foo
}
render () {
return (
<div>{ this.getCalculatedValue() }</div>
)
}
3. [ES6] With a getter property:
Just a variation for the option above, but a little more cleaner since your aren't calling a method, just accessing a property and your function is running "under the table".
get calculatedValue() {
const { foo } = this.props
const { bar } = this.state
return bar ? foo * bar : foo
}
render () {
return (
<div>{ this.getCalculatedValue }</div>
)
}
4. In the parent component, making this one just to show the value:
This one is a little more complex, but usually is better for growing projects. Here we will separate your component in two: The first will calculate (here you can access some API or anywhere your value was coming from) and the child one will just show it's value. This way you can keep the logic and the ui in different components, improving your reuse of code among your project. Is a good pattern to keep your UI components (the child one) in a functional component without state, but this is for another question.
/* A functional component. Is just like a class, but without state */
const MyUIComponent = props => <div>{ props.value }</div>
class MyLogicalComponent extends React.Component {
render(){
/* you can choose any of the options above to calculate */
const computedValue = 5 + 8;
return (
<div>
<MyUIComponent value={ computedValue } />
</div>
)
}
}
Hope it helps!
calculatedValue is a const variable having block scope in your Render function. If you need to use its value in Render function there is no option but to calculate it in Render function only.
componentDidMount = () => {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo //block scope
}
If you declare the variable calculatedValue in componentDidMount() function as shown above then it will not be accessible in Render function which gets called later in the lifecycle of a reactJs component.
render = () => {
//calculatedValue will give undefined error. calculatedValue is not known to render function
return (
<div>{calculatedValue}</div>
)
}
I am trying to use .bind() when using a method in my component.
The reason is simple: In a loop I am returing Components and extend them with a property which is calling a method. But for every loop-item this I want to extend the this Object with some information (like a key).
Example:
Items.jsx
Items = React.createClass({
eventMethod() {
console.log('this event was triggered by key:', this.key);
},
items() {
let items = [];
let properties = {};
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
let {...props} = properties;
let item = <Item {...props} key={key} />;
items.push(item);
});
return items;
},
render() {
return(<div>{this.items()}</div>);
}
});
Item.jsx
Item = React.createClass(...);
In this case (and its working) when the Item Component is triggering the prop "eventMethodInItem" my method "eventMethod" will be called and this.key has the correct value!
So - whats now the question ? Its working perfect, right ?
Yes.
But ReactJS does not want me to do this. This is what ReactJS is telling me as a console log.
Warning: bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See Items
Maybe you think its a "bad" way to add children to the component like I am doing it but in my special case I need to do this in this way - so I need to bind new information to a method.
I'm not going to pretend that I understand what you are trying to do here, but maybe I can help clear it up anyway.
React takes all of the top level methods found on each component and automagically binds them to the context of the component.
This prevents other methods from overriding the context of this and as a result, if you try to rebind the method, React says "Hey don't bother. I already got it" — which is the warning you are seeing.
Assuming that you really want do this (each time you are mutating the outer properties object by overriding the eventMethodInItem property).
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
Then I can't see any reason that the eventMethod has to live on the component, rather than just in the scope of the items function.
items() {
const eventMethod = function() {
console.log('this event was triggered by key:', this.key);
}
// ...
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = eventMethod.bind(_.extend(this, {
key
}));
// ...
});
},
That way you don't have to fight React to get your program to work.
React is already autobinding this when using React.createClass http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#under-the-hood-autobinding-and-event-delegation
Change your binding to
properties.eventMethodInItem = this.eventMethod.bind(null,key);
and your eventMethod to
eventMethod(key) {
console.log('this event was triggered by key:', key);
}
I also suggest using _.map instead of _.each
items() {
return _.map(this.props.items, (itemData, key) => {
return <Item
handleEventMethod={this.eventMethod.bind(null,key)}
key={key} />;
});
},
Good pattern
https://www.newmediacampaigns.com/blog/refactoring-react-components-to-es6-classes
Before :
class ExampleComponent extends React.Component {
constructor() {
super();
this. _handleClick = this. _handleClick.bind(this);
this. _handleFoo = this. _handleFoo.bind(this);
}
// ...
}
After :
class BaseComponent extends React.Component {
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
}
class ExampleComponent extends BaseComponent {
constructor() {
super();
this._bind('_handleClick', '_handleFoo');
}
// ...
}
another good hacks for this topic http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html