zadders
zadders

Reputation: 438

How to update list without refreshing the page

I have an API that provides me with objects that have the following properties: id, title, url, thumbnailUrl.

I have a module that's responsible for the card that has the following code:

export interface Manual {
  id?: string | number;
  url: string;
  title: string;
  thumbnail?: string | null;
}

I have a module that's responsible for creating the actual HelpCard components, and the code for it is as follows:

interface Props {
  card: Manual;
  deleteProduct: Function;
  editProduct: Function;
  type: "videos" | "manuals";
}

interface State {
  editedCard: Manual;
  imageLoading?: boolean;
  tooManyRequests?: boolean;
}

export default class HelpCard extends Component<Props, State> {
  state = {
    editedCard: {
      id: -1,
      url: "",
      title: "",
      thumbnail: ""
    },
    imageLoading: true,
    tooManyRequests: false
  };

  componentDidMount() {}

  onEditClick = (card: Manual): void => {
    this.setState({ editedCard: card });
  };

  updateEditCard = (event: React.FormEvent<HTMLInputElement>, key: "url" | "title" | "thumbnail"): void => {
    const value = event.currentTarget.value;
    this.setState((prevState: State) => {
      return { editedCard: { ...prevState.editedCard, [key]: value } };
    });
  };

  render() {
    const { card } = this.props;
    const { editedCard } = this.state;
    return (
      <React.Fragment>
        <div className="card">
          <div className="card-img">
            {this.state.imageLoading ? <img src={placeholder} style={{ width: "100%", height: "100%" }}></img> : null}
            <img
              className="cardImage"
              style={
                this.state.tooManyRequests
                  ? { display: "none" }
                  : this.state.imageLoading
                  ? { display: "null" }
                  : { display: "block" }
              }
              onLoad={() => this.setState({ imageLoading: false })}
              onError={() => this.setState({ tooManyRequests: true })}
              src={card.thumbnail ? card.thumbnail : placeholder}
            />
          </div>

          <div className="card-body">
            <div style={{ textAlign: "center" }}>
              <span className="card-title">{card.title}</span>
              <hr></hr>
            </div>
            <div className="card-buttons">
              <div className="group">
                <Modal
                  trigger={
                    <Button
                      onClick={() => this.onEditClick(card)}
                      style={{ width: "90px" }}
                      icon="mode_edit"
                      className="btn blue-outline"
                      href="#!">
                      Edit
                    </Button>
                  }>
                  <form
                    onSubmit={(e) => e.preventDefault()}
                    id="videoCardEdit"
                    style={{ width: "auto", height: "auto" }}>
                    <div>
                      <div>
                        <label>Title:</label>
                        <input
                          className="input"
                          style={{ width: "100%" }}
                          name="videoCardTitle"
                          onChange={(e: React.FormEvent<HTMLInputElement>) => this.updateEditCard(e, "title")}
                          value={editedCard.title}></input>
                      </div>
                      <div>
                        <label>URL:</label>
                        <input
                          className="input"
                          style={{ width: "100%" }}
                          name="videoCardURL"
                          onChange={(e: React.FormEvent<HTMLInputElement>) => this.updateEditCard(e, "url")}
                          value={editedCard.url}></input>
                      </div>
                      <div>
                        <label>Thumbnail URL:</label>
                        <input
                          className="input"
                          style={{ width: "100%" }}
                          name="videoCardThumbnail"
                          onChange={(e: React.FormEvent<HTMLInputElement>) => this.updateEditCard(e, "thumbnail")}
                          value={editedCard.thumbnail}></input>
                      </div>
                      <br></br>
                    </div>
                    <Button
                      type="submit"
                      className="btn green-outline"
                      icon="check"
                      length="fullwidth"
                      style={{
                        float: "left"
                      }}
                      onClick={() =>
                        this.props.editProduct(
                          card.id,
                          editedCard.title,
                          editedCard.url,
                          editedCard.thumbnail,
                          this.props.type
                        )
                      }>
                      confirm
                    </Button>
                    <br></br>
                  </form>
                </Modal>
                <Button
                  icon="delete"
                  style={{ width: "90px" }}
                  onClick={() => this.props.deleteProduct(card.id, this.props.type)}
                  className="btn red-outline">
                  Delete
                </Button>
              </div>
            </div>
          </div>
        </div>
      </React.Fragment>
    );
  }
}

The card is then displayed on a HelpList component with the following code:

export default class HelpList extends Component<Props, State> {
  state = {};

  async componentDidMount() {}

  render() {
    console.log(this.props.data);

    return (
      <div className="row">
        {this.props.data.map((card: Manual) => (
          <HelpCard
            card={card}
            key={card.id}
            deleteProduct={this.props.onDelete}
            editProduct={this.props.onEdit}
            type={this.props.type}
          />
        ))}
      </div>
    );
  }
}

The final list in the displayed on my HelpAdmin.view.tsx module which loads my cards, and has all the API functions such as my create / delete / edit calls.

The state and load function is as follows:

export class HelpAdminView extends Component<Props, State> {
  state = {
    title: "",
    thumbnail: "",
    url: "",
    error: null,
    isEditProduct: true,
    isAddProduct: true,
    whichRadioSelected: "video",
    videos: [],
    manuals: []
  };
  componentDidMount() {
    this.loadAdminHelpCard("videos");
    this.loadAdminHelpCard("manuals");
  }

  loadAdminHelpCard = (type: "videos" | "manuals"): void => {
    const url = `${BASE_API_URL}/${type}`;
    axios
      .get(url)
      .then((res) => {
        this.setState({ [type]: res.data } as any);
      })
      .catch(function(error) {
        // handle error
        console.log(error);
      });
  };

Finally, my edit function is like so:

editProduct = (id: any, title: string, url: string, thumbnail: string, type: "videos" | "manuals") => {
    const apiUrl = `${BASE_API_URL}/${type}/${id}`;

    axios
      .put(apiUrl, {
        title: title,
        url: url,
        thumbnail: thumbnail
      })
      .then((response: any) => {
        // ? UPDATE the list with new card information here
      })
      .catch(function(error) {
        console.log(error);
      });
  };

When a user clicks "Edit", the card is copied from props and put into a state. Now only the state of the card is edited. (this.state.editedCard) When users click on submit the editedCard details are given to editProduct(id, title, thumbnail, etc...). In HelpAdmin.view.tsx an API call is being made, that will change the database with the new card details.

What I'm trying to figure out is how to update our card in either videos/manuals so it's reflected in the UI. (ON Successfull API call)

Thank you in advance for reading and helping!

Upvotes: 0

Views: 255

Answers (1)

SuleymanSah
SuleymanSah

Reputation: 17858

You can get the current data, when the update operation ends.

.then((response: any) => {
    //here get the recent data from your api
    // ? UPDATE the list with new card information here
    this.loadAdminHelpCard("videos");
    this.loadAdminHelpCard("manuals");
  })

So this will update the state, causing the parent component rerender, which will also rerender child components, so your Card component will get the new data.

All editProduct function:

  editProduct = (id: any, title: string, url: string, thumbnail: string, type: "videos" | "manuals") => {
    const apiUrl = `${BASE_API_URL}/${type}/${id}`;

    axios
      .put(apiUrl, {
        title: title,
        url: url,
        thumbnail: thumbnail
      })
      .then((response: any) => {
        //here get the recent data from your api
        // ? UPDATE the list with new card information here
        this.loadAdminHelpCard("videos");
        this.loadAdminHelpCard("manuals");
      })
      .catch(function(error) {
        console.log(error);
      });
  };

Upvotes: 1

Related Questions