简体   繁体   中英

React: re render componet after button click

How do I make page refresh or reender content in page after I click submit button? I've trying to put window.location.reload() (I know that not the React way, this.forceUpdate() have same result) in submit functions( closeTicket() , openTicketSubmit() ) but POST request don't get response

OpenTickets.js

import React from "react";
import axios from "axios";

import CardConversation from './CardConversation.jsx';

export default class PersonList extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            people: [],
            send_to_number: "",
            message_body: ""
        };

        this.closeTicket = this.closeTicket.bind(this);
        this.openTicketsReply = this.openTicketsReply.bind(this);
        this.openTicketsSubmit = this.openTicketsSubmit.bind(this);
        this.getPhoneNumberOpenTickets = this.getPhoneNumberOpenTickets.bind(this);
    }

    openTicketsReply = async e => {
        this.setState({
            [e.target.name]: e.target.value
        });
    };

    getPhoneNumberOpenTickets = async e => {
        this.setState({
            send_to_number: e
        });
    };

    openTicketsSubmit = async e => {
        e.preventDefault();
        const formData = new FormData();
        formData.set("send_to_number", this.state.send_to_number.slice(1));
        formData.set("message_body", this.state.message_body);
        axios({
            method: "post",
            url: "/outgoingsms",
            data: formData,
            headers: { "Content-Type": "multipart/form-data" }
        })
    };

    closeTicket = async e => {
        e.preventDefault();
        const formData = new FormData();
        formData.set("customernum", this.state.send_to_number.slice(1));
        axios({
            method: "post",
            url: "/closeticket",
            data: formData,
            headers: { "Content-Type": "multipart/form-data" }
        })
    };


    componentDidMount() {
        this.getPeopleData();
    }

    getPeopleData = async () => {
        try {
            const { data } = await axios.get(`/getongoing?limit=10`);
            this.setState({ people: data });
        } catch (e) {
            console.log("error: ", e);
        }
    };

    render() {
        const {
            closeTicket,
            openTicketsSubmit,
            getPhoneNumberOpenTickets,
            openTicketsReply
        } = this;

        return this.state.people.map(person => (
            <CardConversation
                person={person}
                closeTicket={closeTicket}
                openTicketsSubmit={openTicketsSubmit}
                getPhoneNumberOpenTickets={getPhoneNumberOpenTickets}
                openTicketsReply={openTicketsReply}
            />
        ));
    }
}

CardConversation.jsx

import React, { useCallback, useEffect, useState } from "react";
import { Button, Accordion, Card, Form, Row, Col } from "react-bootstrap";
import axios from "axios";

const CardConversation = ({
                              person,
                              closeTicket,
                              openTicketsSubmit,
                              getPhoneNumberOpenTickets,
                              openTicketsReply,
                          }) => {
    const [conversation, setConversation] = useState([]);

    // Handlers
    const handleSubmit = useCallback(
        e => {
            openTicketsSubmit(e);
        },
        [openTicketsSubmit]
    );

    const handleCloseTicket = useCallback(
        e => {
            closeTicket(e);
        },
        [closeTicket],
    );

    const handleClick = useCallback(() => {
        getPhoneNumberOpenTickets(person);
    },
        [person, getPhoneNumberOpenTickets]);

    const handleChange = useCallback(
        e => {
            openTicketsReply(e);
        },
        [openTicketsReply]
    );

    // Methods
    const fetchConversation = useCallback(async () => {
        try {
            const { data } = await axios.get(
                "/getconvfornum?customer_number=" + person.slice(1)
            );
            setConversation(data);
        } catch (e) {
            console.log("error: ", e);
        }
    }, [person, conversation]);

    // Effects
    useEffect(() => {
        fetchConversation(person)
    }, [person]);

    return (
        <Accordion defaultActiveKey="0">
            <Card>
                <Card.Header>
                    <Accordion.Toggle as={Button} variant="button" eventKey="0">
                        Conversation {person.indexOf(person)+1+ '    '}
                        Phone number: {person}
                    </Accordion.Toggle>
                </Card.Header>
                <Accordion.Collapse eventKey="0">
                    <Card.Body>
                        {conversation.map(message => (
                            <div>
                                <p>{message.from}</p>
                                <p>{message.body}</p>
                            </div>
                        ))}
                        <Form onSubmit={handleSubmit}>
                            <br />
                            <Form.Group as={Row} controlId="formPlaintextPassword">
                                <Col sm="10">
                                    <Form.Control
                                        type="text"
                                        placeholder="Reply"
                                        name="message_body"
                                        onChange={handleChange}
                                    />
                                </Col>
                                <Button type={"submit"}
                                        onClick={handleClick} column sm="2">
                                    Reply
                                </Button>
                            </Form.Group>
                        </Form>
                        <Form onSubmit={handleCloseTicket}>
                            <Form.Group>
                                <Col sm="11">
                                    <Button type={"submit"}
                                            onClick={handleClick} column sm="4">
                                        Close Ticket
                                    </Button>
                                </Col>
                            </Form.Group>
                        </Form>
                    </Card.Body>
                </Accordion.Collapse>
            </Card>
            <br />
        </Accordion>
    );
};

export default CardConversation;

A simple way to re-render would be to change the state variable on the submission of the Axios request causing the component to re-render automatically. Example:

axios({...}).then(resp => {
 this.setState({message_body:'',send_to_number:''}); // will cause to re-render
})

You can make the component re-render by updating its state ( after the POST ) :

closeTicket = async e => {
  e.preventDefault();
  const formData = new FormData();
  formData.set("customernum", this.state.send_to_number.slice(1));
  axios({
      method: "post",
      url: "/closeticket",
      data: formData,
      headers: { "Content-Type": "multipart/form-data" }
  })
  .then(() => {
    this.setState({ /* */ })
    // or
    // this.forceUpdate();
  })
};

React will rerender components once the state changes, which means that what you need is to change the state once the submit button is clicked. Since the submit button is inside the PersonList component and you also want to reload PersonList, you want to change the state of PersonList when the submit button is clicked.

Here is what you might want to do: 1) add a 'reload' state to PersonList, defaulting it to false. This will tell the component if you need to reload or not. 2) pass a function that sets the state of PersonList's reload value into the child component, in this case CardConversion. something like this.setState({reload:!this.state.reload}) should do. 3) when you finish handling what you need to handle within CardConversion, call your passed function to set the parent's state value, and the whole component should reload.

this.state = {
  reload: false
  ...
}

...

shouldReload() {
  this.setState({reload:!this.state.reload});
}

...

<CardConversation
  person={person}
  closeTicket={closeTicket}
  openTicketsSubmit={openTicketsSubmit}
  getPhoneNumberOpenTickets={getPhoneNumberOpenTickets}
  openTicketsReply={openTicketsReply}
  reloadParent={this.shouldReload.bind(this)}
/>

and on CardConversation

const handleClick = useCallback(() => {
  getPhoneNumberOpenTickets(person);
  this.props.reloadParent();
},

in opentickets.js

create a function updatePeople and in that function call this.getPeopleData();
eg

updatePeople = () =>{
    this.getPeopleData();
  }

then in

 return this.state.people.map(person => (
            <CardConversation
                person={person}
                closeTicket={closeTicket}
                openTicketsSubmit={openTicketsSubmit}
                getPhoneNumberOpenTickets={getPhoneNumberOpenTickets}
                openTicketsReply={openTicketsReply}
                refreshPeople = {this.updatePeople}

            />
        ));

in cardConversion.jsx

when you click the close button or whichever button gets you back to openTickets, put the callback function

this.props.refreshPeople();

because you have componentDidMount, everytime you call whatever is in componentDidMount, it will update the information and re render it

First of all, regarding you are not getting any data issue, you can check axios , and how they use post:

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    // where you can setState here
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Mainly axios are handling the data asynchronously. By that, once you call the api, React will executes next line of code.

For more discussion of how to force update the component, you can check this post: Can you force a React component to rerender without calling setState? , which explain how to update component very well.

As far as I can see, what you're trying to do is reload the people list. If that's the case, you can solve it in two ways:

  1. In both axios API calls, add .then() block and call this.getPeopleData() .
  2. Instead of re-fetching people's data, you get the added/deleted post data and update state in the .then() block with setState() .

I suggest you to adopt Option 2, because fetching the list again will require more time for you to get the refreshed list.

Either way, just adding this.forceUpdate() to your .then() block will not give you the updated list. It won't do anything actually to the UI. (though it makes it re-render)

CardConversatio.jsx

import React, { useCallback, useEffect, useState } from "react";
import { Button, Accordion, Card, Form, Row, Col } from "react-bootstrap";
import axios from "axios";

const CardConversation = ({
  person,
  closeTicket,
  openTicketsSubmit,
  getPhoneNumberOpenTickets,
  openTicketsReply,
  getPhoneToCloseTicket,
}) => {
  const [conversation, setConversation] = useState([]);
  const [trigger, fireUpdate] = useState(false);

  // Handlers

  const renderConversation = useCallback(() => {
    return conversation.map(message => (
      <div key={message.date.$date + person}>
        <p>{message.from}</p>
        <p>{message.body}</p>
      </div>
    ));
  }, [conversation, person]);

  const fetchConversation = useCallback(async () => {
    try {
      const { data } = await axios.get(
        "/getconvfornum?customer_number=" + person.slice(1)
      );
      setConversation(data);
      console.log("fetch ", data);
    } catch (e) {
      console.log("error: ", e);
    }
  }, [person]);

  const handleClick = useCallback(async () => {
    await getPhoneNumberOpenTickets(person);
    setTimeout(() => fetchConversation(person), 500);
  }, [getPhoneNumberOpenTickets, person, fetchConversation]);

  const handleClickClose = useCallback(async () => {
    await getPhoneToCloseTicket(person);
  }, [person, getPhoneToCloseTicket]);


  const handleChange = useCallback(
    e => {
      openTicketsReply(e);
    },
    [openTicketsReply]
  );

  useEffect(() => {
    console.log("effect");
    fetchConversation(person);
  }, [fetchConversation, person]);

  return (
    <Accordion defaultActiveKey="0">
      <Card>
        <Card.Header>
          <Accordion.Toggle as={Button} variant="button" eventKey="0">
            Conversation {person.indexOf(person) + 1 + "    "}
            Phone number: {person}
          </Accordion.Toggle>
        </Card.Header>
        <Accordion.Collapse eventKey="0">
          <Card.Body>
            {renderConversation()}
            <Form>
              <br />
              <Form.Group as={Row} controlId="formPlaintextPassword">
                <Col sm="10">
                  <Form.Control
                    type="text"
                    placeholder="Reply"
                    name="message_body"
                    onChange={handleChange}
                  />
                </Col>
                <Button onClick={handleClick} column sm="2">
                  Reply
                </Button>
              </Form.Group>
            </Form>
            <Form>
              <Form.Group>
                <Col sm="11">
                  <Button onClick={handleClickClose} column sm="4">
                    Close Ticket
                  </Button>
                </Col>
              </Form.Group>
            </Form>
          </Card.Body>
        </Accordion.Collapse>
      </Card>
      <br />
    </Accordion>
  );
};

export default CardConversation;

OpenTickets.js

import React from "react";
import axios from "axios";

import CardConversation from './CardConversation.jsx';

export default class PersonList extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            people: [],
            send_to_number: "",
            message_body: "",
            closed: false
        };

        this.closeTicket = this.closeTicket.bind(this);
        this.openTicketsReply = this.openTicketsReply.bind(this);
        this.openTicketsSubmit = this.openTicketsSubmit.bind(this);
        this.getPhoneNumberOpenTickets = this.getPhoneNumberOpenTickets.bind(this);
        this.getPhoneToCloseTicket = this.getPhoneToCloseTicket.bind(this);
    }

    openTicketsReply = async e => {
        e.preventDefault();
        this.setState({
            message_body: e.target.value
        });
    };

    getPhoneNumberOpenTickets = async e => {
        //e.preventDefault();
        this.setState({
            send_to_number: e
        }, async () => await this.openTicketsSubmit());
    };

    getPhoneToCloseTicket = async e => {
        this.setState({
            send_to_number: e
        }, async () => this.closeTicket());
    };

    openTicketsSubmit = async e => {
        const formData = new FormData();
        formData.set("send_to_number", this.state.send_to_number.slice(1));
        formData.set("message_body", this.state.message_body);
        axios({
            method: "post",
            url: "/outgoingsms",
            data: formData,
            headers: { "Content-Type": "multipart/form-data" }
        }).then(resp => {
            this.setState({ closed: true });
        }).catch(error => console.log(error))
    };

    closeTicket = async e => {
        const formData = new FormData();
        formData.set("customernum", this.state.send_to_number.slice(1));
        axios({
            method: "post",
            url: "/closeticket",
            data: formData,
            headers: { "Content-Type": "multipart/form-data" }
        }).then(resp => {
            this.setState({ closed: true });
        }).catch(error => console.log(error))
    };


    componentDidMount() {
        this.getPeopleData();
    }

    getPeopleData = async () => {
        try {
            const { data } = await axios.get(`/getongoing?limit=10`);
            this.setState({ people: data });
        } catch (e) {
            console.log("error: ", e);
        }
    };

    render() {
        const {
            closeTicket,
            getPhoneNumberOpenTickets,
            openTicketsReply,
            getPhoneToCloseTicket
        } = this;

        return this.state.people.map(person => (
            <CardConversation
                key={person}
                person={person}
                closeTicket={closeTicket}
                getPhoneNumberOpenTickets={getPhoneNumberOpenTickets}
                openTicketsReply={openTicketsReply}
                getPhoneToCloseTicket={getPhoneToCloseTicket}
            />
        ));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM