【问题标题】:this.props.history.push not re-rendering react componentthis.props.history.push 不重新渲染反应组件
【发布时间】:2018-12-27 06:36:32
【问题描述】:

在我的组件中,我使用 this.props.history.push(pathname:.. search:..) 重新渲染组件并从第三方服务获取新数据。当我第一次调用它呈现的页面时。但是,当我在组件内部调用历史推送时,URL 会正确更新,但组件不会重新呈现。我读了很多,但无法让它工作。有什么想法吗?

我正在使用反应路由器 v4

//index.js

  <Provider store={store}>
    <BrowserRouter>
      <Switch>
        <Route path="/login" component={Login}/>
        <Route path="/" component={Main}/>
      </Switch>
    </BrowserRouter>
  </Provider>


//Main.js
//PropsRoute is used to push props to logs component so I can use them when fetching new data
const PropsRoute = ({ component: Component, ...rest }) => {
  return (
    <Route {...rest} render={props => <Component {...props} />}/>
  );
};

class Main extends Component {
  render() {
    return (
        <div className="app">
          <NavigationBar/>
          <div className="app-body">
            <SideBar/>
            <Switch>
              <PropsRoute path="/logs" component={Log}/> //this component is not rerendering
              <Route path="/reports" component={Reports}/>
              <Route path="/gen" component={Dashboard}/>
              <Redirect from="/" to="/gen"/>
            </Switch>
          </div>
        </div>
    )
  }
}

export default Main;


//inside 'Log' component I call
import React, {Component} from 'react';
import {getSystemLogs} from "../api";
import {Link} from 'react-router-dom';
import _ from "lodash";
import queryString from 'query-string';

let _isMounted;

class Log extends Component {

  constructor(props) {
    super(props);

    //check if query params are defined. If not re render component with query params
    let queryParams = queryString.parse(props.location.search);
    if (!(queryParams.page && queryParams.type && queryParams.pageSize && queryParams.application)) {
      this.props.history.push({
        pathname: '/logs',
        search: `?page=1&pageSize=25&type=3&application=fdce4427fc9b49e0bbde1f9dc090cfb9`
      });
    }

    this.state = {
      logs: {},
      pageCount: 0,
      application: [
        {
          name: 'internal',
          id: '...'
        }
      ],
      types: [
        {
          name: 'Info',
          id: 3
        }
      ],
      paginationPage: queryParams.page - 1,
      request: {
        page: queryParams.page === undefined ? 1 : queryParams.page,
        type: queryParams.type === undefined ? 3 : queryParams.type,
        pageSize: queryParams.pageSize === undefined ? 25 : queryParams.pageSize,
        application: queryParams.application === undefined ? 'fdce4427fc9b49e0bbde1f9dc090cfb9' : queryParams.application      
      }
    };

    this.onInputChange = this.onInputChange.bind(this);
  }

  componentDidMount() {
    _isMounted = true;
    this.getLogs(this.state.request);
  }

  componentWillUnmount() {
    _isMounted = false;
  }

  getLogs(request) {
    getSystemLogs(request)
      .then((response) => {
        if (_isMounted) {
          this.setState({
            logs: response.data.Data,
            pageCount: (response.data.TotalCount / this.state.request.pageSize)
          });
        }
      });
  }

  applyFilter = () => {
    //reset page to 1 when filter changes
    console.log('apply filter');
    this.setState({
      request: {
        ...this.state.request,
        page: 1
      }
    }, () => {
      this.props.history.push({
      pathname: '/logs',
        search: `?page=${this.state.request.page}&pageSize=${this.state.request.pageSize}&type=${this.state.request.type}&application=${this.state.request.application}`
      });
    });
  };

  onInputChange = () => (event) => {
    const {request} = this.state; //create copy of current object
    request[event.target.name] = event.target.value; //update object
    this.setState({request}); //set object to new object
  };

  render() {
    let logs = _.map(this.state.logs, log => {
      return (
          <div className="bg-white rounded shadow mb-2" key={log.id}>
           ...
          </div>
      );
    });

    return (
      <main className="main">
        ...
      </main>
    );
  }
}

export default Log;

【问题讨论】:

  • 你的日志组件可以吗
  • 我添加了日志组件。函数 applyFilter 调用 history.push。如前所述,URL 得到更新,但组件没有重新渲染。我希望重新渲染,以便再次调用构造函数并且我可以获得新数据。这是正确的方法吗?谢谢。
  • 在查询参数更改时触发重新渲染,但不是重新挂载,因此不会再次调用构造函数。如果您想检测查询参数的变化,请检查此stackoverflow.com/questions/48993247/…

标签: reactjs react-router react-router-v4


【解决方案1】:

propsstate 更改时,Reactjs 不会重新运行 constructor 方法,当您第一次调用组件时,他会调用 constructor

如果您的nextProps.location.pathname 与您的this.props.location.pathname (react-router location) 不同,您应该使用componentDidUpdate 并进行提取

【讨论】:

    【解决方案2】:

    getderivedstatefromprops 生命周期方法创建一个容器组件/提供者怎么样,它更像反应:

    class ContainerComp extends Component {
      state = { needRerender: false };
    
      static getderivedstatefromprops(nextProps, nextState) {
        let queryParams = queryString.parse(nextProps.location.search);
    
        if (!(queryParams.page && queryParams.type && queryParams.pageSize && queryParams.application)) {
          return { needRefresh: true };
        } else {
          return { needRefresh: false };
        }
    
      }
    
      render() {
        return (
          <div>
            {this.state.needRefresh ? <Redirect params={} /> : <Log />}
          </div>
        );
      }
    }
    

    【讨论】:

      【解决方案3】:

      我在功能组件上遇到了同样的问题,我使用钩子 useEffect 和 props.location 作为依赖项解决了它。

        import React, { useEffect } from 'react';
        const myComponent = () => {
          useEffect(() => {
            // fetch your data when the props.location changes
          }, [props.location]);
        } 
      
      
      

      每当props.location 发生变化时,这将调用useEffect,以便您获取数据。它的作用类似于componentDidMountcomponentDidUpdate

      【讨论】:

      • 我尝试使用你的这个验证,但我没有成功
      • 您对可能导致屏幕呈现的原因还有其他建议吗?
      • 这是正确的答案,但在这种情况下,API 在每次渲染中调用两次。
      猜你喜欢
      • 2020-03-08
      • 2018-09-19
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      • 2020-12-17
      相关资源
      最近更新 更多