兄弟重新渲染时如何使React组件不重新渲染?

时间:2020-06-28 12:43:30

标签: javascript reactjs redux react-redux

好,所以我才开始玩react和redux,遇到了问题。我有一个调用API的按钮,并接收有关某些汽车的信息。它是这样的:

在按钮上单击它会调度功能getCars(), 这依次发送pendingAction,然后获取信息,然后发送successActionerrorAction。 (我将在下面显示所有代码)。

我的问题是:

在加载新信息时,即使它们具有相同的src,也会将状态更改为待定,然后重新渲染。我希望避免重新渲染,因为这样会使图片闪烁白色。

Sample

我的应用程序设置如下:

//index.js

import App from './App';
import * as serviceWorker from './serviceWorker';
import { applyMiddleware, createStore, compose } from 'redux';
import { Provider } from 'react-redux';
import Reducers from './/Reducers';
import thunk from 'redux-thunk';

const middlewares = [thunk];

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(Reducers, composeEnhancers(
    applyMiddleware(...middlewares)
));  

ReactDOM.render(
   <Provider store={store}>
    <App />
   </Provider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: somelink 
serviceWorker.unregister();

然后我的app.js

//App.js

import React from 'react';
import 'rsuite/dist/styles/rsuite-default.css';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, login, logout } from './/Actions/TestingActions';
import  GetCars  from './/API/Cars/GetCars';
import { Button } from 'rsuite';
import CarView from './Components/CarTestView/CarView'
//import './index.css';


function App() {
    const counter = useSelector(state => state.count)
    const logged = useSelector(state => state.loggedin)
    const dispatch = useDispatch()

    return (
    <div className="App">
            //  
            //   I hidden some unrelated code here  ...              
            //  
            <Button onClick={() => dispatch(GetCars())}>Getcars</Button>
            <CarView />
    </div>
  );
}

export default App;

GetCars ...

//GetCars.js

import { apiCarsError, apiCarsSuccess, apiCarsPending } from '../../Actions/TestingActions';
export function GetCars() {    
    return dispatch => {
        dispatch(apiCarsPending());
        fetch('https://localhost:44342/API/GetRandomCar')
            .then(res => {
                res.json().then(res => {
                    if (res.error) {
                        throw (res.error);
                    }
                    dispatch(apiCarsSuccess(res));
                    return res;
                })
                    .catch(error => {
                        dispatch(apiCarsError(error));
                    })
            });
            
    }
}

export default GetCars;

最后是CarView。

import React from 'react';
import { connect, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { GetCars } from '../../API//Cars//GetCars';
import { getCars, getCarsPending, getCarsError } from '../../Reducers//TestingReducer';
import { Loader, Placeholder, Panel, /*PanelGroup*/ } from 'rsuite';
//import { CSSTransition, TransitionGroup  } from 'react-transition-group';
const { Paragraph } = Placeholder;

function CarView() {
    const pending = useSelector(state => state.API.pending)
    //const error = useSelector(state => state.API.error)
    const cars = useSelector(state => state.API.cars)
    if (pending && cars.length === 0) return (    
        <div>
            {console.log("Update is nulio")}
                <Loader backdrop content="loading..." vertical />
                <Paragraph rows={8}></Paragraph>       
            </div>          
        )

    if (pending) return (   
        <div>
            {console.log("Update pending")}
                <Loader center content="keiciam metus" />
            <div>
                    <Carvaizdas cars={cars} updatePicture={false} />
            </div>
            </div>
        )

    if (cars.length === 0) return (<div>{console.log("tuscia")}</div>)

    return (
        <div>               
            {console.log("uzkrauta || new info")}
                <div>
                    <Carvaizdas cars={cars} updatePicture={true} />
                </div>
            </div>
        )
}

class Carvaizdas extends React.PureComponent {

    shouldComponentUpdate() {      
        console.log("Should render ?"); 
        console.log(this.props.updatePicture); 
        return this.props.updatePicture;
    }

    render() {
        console.log("render cars");        
        return (
            <>
       <h1>Masinos</h1>
                {this.props.cars.map(car => <CarKorta car={car}/>)}
            </>
        );
    }
}

class CarKorta extends React.PureComponent {
    render() {
        return (
            <Panel shaded bordered bodyFill style={{ display: 'inline-block', width: 240, margin: 10 }}>
                <div style={{ height: 150, width: 240, display: 'flex', alignItems: 'center', justifyContent: 'center', paddingTop: 10 }}>
                    <div style={{ height: 'auto', width: 220 }}>
                        <img src={this.props.car.picture} /*height="240"*/ style={{ maxHeight: 150, height: 'auto', width: 220, borderRadius: 5, boxShadow: "1px 1px 2px #666" }} />
                    </div>
                </div>
                <Panel header={this.props.car.make}>
                    <p>
                        Year: {this.props.car.year}
                        <br />
                        Model: {this.props.car.model}
                    </p>
                </Panel>
            </Panel>
        );
    }
}

const mapStateToProps = state => ({
    error: getCarsError(state),
    cars: getCars(state),
    pending: getCarsPending(state)
})

const mapDispatchToProps = dispatch => bindActionCreators({
    CarView: GetCars()
}, dispatch)

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(CarView);
    
        

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

问题在于在不同条件下使用组件Carvaizdas的2个实例。这样就没有shouldComponentUpdate钩子的意义,后者是特定于PER INSTANCE的。

if (pending)
  return (
    <div>
      {console.log("Update pending")}
      <Loader center content="keiciam metus" />
      <div>
        <Carvaizdas cars={cars} updatePicture={false} /> {/** first component instance */}
      </div>
    </div>
  );

if (cars.length === 0) return <div>{console.log("tuscia")}</div>;

return (
  <div>
    {console.log("uzkrauta || new info")}
    <div>
      <Carvaizdas cars={cars} updatePicture={true} /> {/** second component instance */}
    </div>
  </div>
);

要使shouldComponentUpdate正常工作,应该只有一个实例

return (
  <div>
    {console.log("uzkrauta || new info")}
    <div>
      <Carvaizdas cars={cars} />
    </div>
  </div>
);

在此组件中,使用shouldComponentUpdate也没有意义

class Carvaizdas extends React.PureComponent {
  render() {
      console.log("render cars");        
      return (
          <>
     <h1>Masinos</h1>
              {this.props.cars.map(car => <CarKorta car={car}/>)}
          </>
      );
  }
}

这仅对CarKorta有意义。您应该从shouldComponentUpdate中删除Carvaizdas并将其添加到CarKorta中。另外,您必须以CarKorta状态存储上一张图片,以便能够与下一张图片进行比较。为此,您必须使用getDerivedStateFromProps

class CarKorta extends React.PureComponent {
  state = {
    car: null,
  };

  shouldComponentUpdate(nextProps) {
    return !this.state.car || this.state.car.picture !== nextProps.car.picture;
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    return {
      car: { ...nextProps.car },
    };
  }

  render() {
    return (
      <Panel
        shaded
        bordered
        bodyFill
        style={{ display: "inline-block", width: 240, margin: 10 }}
      >
        <img
          src={this.state.car.picture}
          /*height="240"*/ style={{
            maxHeight: 150,
            height: "auto",
            width: 220,
            borderRadius: 5,
            boxShadow: "1px 1px 2px #666",
          }}
        />
      </Panel>
    );
  }
}

答案 1 :(得分:0)

我这样更改CarView,现在可以使用了。

import React from 'react';
import { connect, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { GetCars } from '../../API//Cars//GetCars';
import { getCars, getCarsPending, getCarsError } from '../../Reducers//TestingReducer';
import { Loader, Placeholder, Panel, /*PanelGroup*/ } from 'rsuite';
//import { CSSTransition, TransitionGroup  } from 'react-transition-group';
const { Paragraph } = Placeholder;




function CarView() {
    const pending = useSelector(state => state.API.pending)
    //const error = useSelector(state => state.API.error)
    const cars = useSelector(state => state.API.cars)
    if (pending && cars.length === 0) return (    
        <div>
            {console.log("Update is nulio")}
                <Loader backdrop content="loading..." vertical />
                <Paragraph rows={8}></Paragraph>       
            </div>          
        )

    if (cars.length === 0) return (<div>{console.log("tuscia")}</div>)

    return (
        <div>               
            {console.log("uzkrauta || new info")}
            {pending ? <Loader center content="keiciam metus" />: <></>}
                <div>
                    <Carvaizdas cars={cars} />
                </div>
            </div>
        )
}

class Carvaizdas extends React.PureComponent {
    render() {
        console.log("render cars");        
        return (
            <>
        <h1>Masinos</h1>
                {this.props.cars.map(car => <CarKorta car={car}/>)}
            </>
        );
    }
}

class CarKorta extends React.PureComponent {
    render() {
        return (
            <Panel shaded bordered bodyFill style={{ display: 'inline-block', width: 240, margin: 10 }}>
                <div style={{ height: 150, width: 240, display: 'flex', alignItems: 'center', justifyContent: 'center', paddingTop: 10 }}>
                    <div style={{ height: 'auto', width: 220 }}>
                        <img src={this.props.car.picture} /*height="240"*/ style={{ maxHeight: 150, height: 'auto', width: 220, borderRadius: 5, boxShadow: "1px 1px 2px #666"     }} />
                    </div>
                </div>
                <Panel header={this.props.car.make}>
                        <p>
                        Year: {this.props.car.year}
                        <br />
                        Model: {this.props.car.model}
                    </p>
                </Panel>
            </Panel>
        );
    }
}

const mapStateToProps = state => ({
    error: getCarsError(state),
    cars: getCars(state),
    pending: getCarsPending(state)
})

const mapDispatchToProps = dispatch => bindActionCreators({
    CarView: GetCars()
}, dispatch)

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(CarView);