Home > other >  react state not updated even inside callback
react state not updated even inside callback

Time:12-31

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'
    });
  });
}
  • Related