I'm having problem setting state. I've to pass extra classes to CardComponent
on click, on click is working fine, setState
is called, and the callback
executes, but state is not mutated (as logs clearly deny that state is updated - see onSelect
below). How could I fix that?
Flow is that if an item is selected, parent component resets the array with selected property set on each item (the selected result object gets selected
set to true
), that works fine and list is reset, selected is highlighted. Then if user clicks on another item (SearchResult component), it should update itself and apply extra classes. This time it's guarenteed that parent would not reset the list.
import { Component, ReactNode } from "react";
import { Subject, Subscription } from "rxjs";
import CardComponent from "../../utils/card.component";
export interface SearchResultComponentClickEvent {
(_id: string): void;
}
export interface SearchResultComponentProps {
result: any;
full: boolean;
onClick: SearchResultComponentClickEvent;
onSelect: Subject<string>;
}
interface SearchResultComponentState {
selected: string;
extraClasses: string;
}
export default class SearchResult extends Component<
SearchResultComponentProps,
SearchResultComponentState
> {
onSelectSubscription: Subscription = null;
constructor(props: SearchResultComponentProps) {
super(props);
let extraClasses = "mb-4";
if (this.props.result.selected) extraClasses = " border border-primary";
this.state = {
selected: this.props.result.selected,
extraClasses,
};
this.onSelect = this.onSelect.bind(this);
}
componentDidMount(): void {
this.onSelectSubscription = this.props.onSelect.subscribe((_id: string) => {
this.setState({
selected: '',
extraClasses: 'mb-4'
});
});
}
componentWillUnmount(): void {
this.onSelectSubscription.unsubscribe();
}
onSelect() {
this.setState({
selected: this.props.result._id,
extraClasses: "mb-4 border border-primary",
}, () => {
console.log(this.state);
// logs: { selected: '', extraClasses: "mb4 " }
});
this.props.onClick(this.props.result._id);
}
view(): ReactNode {
return <div>Simple view</div>
}
fullView(): ReactNode {
return <div>Extended view</div>;
}
render(): ReactNode {
return (
<CardComponent
padding={0}
onClick={this.onSelect}
extraClasses={this.state.extraClasses}
key={this.state.extraClasses}
>
{this.props.full ? this.fullView() : this.view()}
</CardComponent>
);
}
}
CodePudding user response:
You have changed the state but setState is asynchronous in nature, changing the state will not immediately give its updated value https://facebook.github.io/react/docs/react-component.html#setstate, if you can show snippet of what exactly are you doing CardComponent, can give a more reliable solution. If you still want to go with this approach, you can change your onSelect to something like this.
onSelect() {
this.setState({
selected: this.props.result._id,
extraClasses: "mb-4 border border-primary",
}, function() {
console.log(this.state);
this.props.onClick(this.props.result._id);
});
}
this will wrap or attached a callback to the state, which guarantees it, but it has its own side effects.
I would also recommend componentDidUpdate
over this solution, more precise.
CodePudding user response:
The problem is probably in the rxjs Subscription object. Insert console.log and check...
componentDidMount(): void {
this.onSelectSubscription = this.props.onSelect.subscribe((_id: string) => {
// Log
console.log('componentDidMount-onSelectSubscription', this.state);
this.setState({
selected: '',
extraClasses: 'mb-4'
});
});
}