【问题标题】:React: re render componet after button clickReact:单击按钮后重新渲染组件
【发布时间】:2020-05-21 17:15:23
【问题描述】:

点击提交按钮后如何刷新页面或重新渲染页面内容?我试图将window.location.reload()(我知道不是 React 方式,this.forceUpdate() 具有相同的结果)放入提交函数(closeTicket()openTicketSubmit())但 POST 请求没有得到响应

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;

【问题讨论】:

  • 大概当你关闭一个工单时,它会导致某种形式的状态被改变?例如ticket.closed = true?
  • 我试过了,但没用
  • @MiXProjectT 显示你做了什么?
  • 我在this.state 中添加closed: false 并在axios 之后执行then(() =&gt; { this.setState({ closed: true}); } 并删除e.preventDefault()
  • @MiXProjectT 太早了,给你的 Axios 调用添加一个回调,然后在那里运行setState

标签: javascript reactjs axios


【解决方案1】:

opentickets.js

创建一个函数 updatePeople 并在该函数中调用this.getPeopleData();
例如

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

然后在

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

            />
        ));

cardConversion.jsx

当你点击关闭按钮或任何一个让你回到 openTickets 的按钮时,放置回调函数

this.props.refreshPeople();

因为你有componentDidMount,所以每次调用componentDidMount中的任何东西,它都会更新信息并重新渲染它

【讨论】:

    【解决方案2】:

    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}
                />
            ));
        }
    }
    

    【讨论】:

      【解决方案3】:

      一旦状态发生变化,React 将重新渲染组件,这意味着您需要在单击提交按钮后更改状态。由于提交按钮位于 PersonList 组件内,并且您还希望重新加载 PersonList,因此您希望在单击提交按钮时更改 PersonList 的状态。

      您可能想要执行以下操作: 1) 为 PersonList 添加一个“重新加载”状态,默认为 false。这将告诉组件您是否需要重新加载。 2) 将设置 PersonList 的重载值状态的函数传递给子组件,在本例中为 CardConversion。像 this.setState({reload:!this.state.reload}) 应该做的事情。 3)当你完成了你需要在 CardConversion 中处理的事情时,调用你传递的函数来设置父级的状态值,整个组件应该重新加载。

      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)}
      />
      

      在 CardConversation 上

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

      【讨论】:

      • const handleClick 中说this.props.reloadParent(); 是未解决的函数或方法。我试图修复它,但仍然没有结果。这就是代码现在的样子link
      • @mixproject 您的链接没有工作示例,但我尝试快速扫描代码,看看是否能发现问题。可能是您没有将“this”绑定到 shouldReload 函数中吗?另外,请通过将它们输出到控制台来检查您传递的道具:console.log(this.props)。提前致谢
      【解决方案4】:

      据我所知,您要做的是重新加载人员列表。如果是这种情况,你可以通过两种方式解决:

      1. 在两个axios API 调用中,添加.then() 块并调用this.getPeopleData()
      2. 您无需重新获取人们的数据,而是使用setState().then() 块中获取添加/删除的帖子数据并更新状态。

      我建议您采用选项 2,因为再次获取列表将需要更多时间来获取刷新的列表。

      无论哪种方式,只是将this.forceUpdate() 添加到您的.then() 块中不会为您提供更新的列表。它实际上不会对 UI 做任何事情。 (尽管它会重新渲染)

      【讨论】:

        【解决方案5】:

        首先,关于您没有收到任何数据问题,您可以查看axios,以及他们如何使用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);
          });
        

        主要是 axios 异步处理数据。这样,一旦你调用了 api,React 就会执行下一行代码。

        更多关于如何强制更新组件的讨论,可以查看这篇文章:Can you force a React component to rerender without calling setState?,里面很好的解释了如何更新组件。

        【讨论】:

          【解决方案6】:

          重新渲染的一种简单方法是在提交 Axios 请求时更改状态变量,从而导致组件自动重新渲染。 示例:

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

          【讨论】:

            【解决方案7】:

            您可以通过更新其state(在 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();
              })
            };
            

            【讨论】:

            • 我在this.state 中添加closed: false 并且不重新渲染then(() =&gt; { this.setState({ closed: true}); }) 页面
            • 更新状态将刷新组件,如果您希望页面刷新,请从 openTicketsSubmit 中删除 e.preventDefault();,但我不建议这样做,请尝试更新 openTicketsSubmit 中的状态,如closeTicket
            • 我删除'e.preventDefault();'但它甚至不再发送帖子请求了
            猜你喜欢
            • 2020-06-06
            • 1970-01-01
            • 1970-01-01
            • 2019-05-22
            • 2020-11-24
            • 1970-01-01
            • 2022-08-13
            • 2017-11-30
            • 1970-01-01
            相关资源
            最近更新 更多