Reputation: 1161
on button click it dispatches function getCars()
,
this in turn dispatches pendingAction
, then fetches info, then dispatches successAction
or errorAction
. (I'll show all code bellow).
while loading new info it changes states to pending, and then re-renders pictures, even though they have same src
. I want, to avoid re-render as, it makes pictures flash to white for a second.
I have my app set up like this:
//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();
Then my 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;
finally 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);
Thanks for your help.
Upvotes: 0
Views: 316
Reputation: 1161
I change CarView like this and it works now.
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);
Upvotes: 0
Reputation: 2866
The problem is in using 2 instances of component Carvaizdas
under a different condition. This makes no sence for shouldComponentUpdate
hook which is specific 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>
);
In order to shouldComponentUpdate
to work there should be only single instance
return (
<div>
{console.log("uzkrauta || new info")}
<div>
<Carvaizdas cars={cars} />
</div>
</div>
);
And in this component using shouldComponentUpdate
makes no sense too
class Carvaizdas extends React.PureComponent {
render() {
console.log("render cars");
return (
<>
<h1>Masinos</h1>
{this.props.cars.map(car => <CarKorta car={car}/>)}
</>
);
}
}
It only makes sense for CarKorta
. You should remove shouldComponentUpdate
from Carvaizdas
and add it to CarKorta
. Also you will have to store the previous picture in CarKorta
state in order to be able to compare it with next picture. For this you have to use 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>
);
}
}
Upvotes: 1