forceUpdate warning on Grid - reactjs

I am getting the warning below when I force update on a grid like this:
proxyConsole.js:56 Warning: forceUpdate(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
Not sure how to amend where to put forceUpdate() so that it behaves correctly.
const quoteList = [];
class App extends Component {
constructor() {
super();
this.state = {
response: {},
endpoint: "http://127.0.0.1:4001"
};
}
componentDidMount() {
const { endpoint } = this.state;
const socket = socketIOClient(endpoint);
this.theGrid.forceUpdate();
socket.on("FromAPI", data => this.setState({ response: data }));
}
cellRenderer ({ columnIndex, key, rowIndex, style }) {
return (
<div
key={key}
style={style}
>
{quoteList[rowIndex][columnIndex]}
</div>
)
}
render() {
const { response } = this.state;
if(Object.keys(response).length !== 0)
{
//console.log(response);
const row = symbolRowDictionary[response.ticker];
var column = 0;
switch(response.type)
{
case 'bid':
column = 1;
break;
case 'ask':
column = 3;
break;
case 'last':
column = 5;
break;
default:
console.log('Unknown type');
}
quoteList[row][column] = response.size;
quoteList[row][column + 1] = response.price;
this.theGrid.forceUpdate();
}
return (
<div style={{ textAlign: "center" }}>
{
<ul><li>Quote: {response.ticker} {response.type} {response.price} {response.size}</li></ul>
}
<div>
</div>
<Grid
ref={(ref) => this.theGrid = ref}
cellRenderer={this.cellRenderer}
columnCount={7}
columnWidth={75}
height={quoteList.length * 20}
rowCount={quoteList.length}
rowHeight={20}
width={800}
/>
</div>
);
}
}
export default App;

I would move quoteList to component state as following:
const endpoint: "http://127.0.0.1:4001";
class App extends Component {
constructor() {
super();
this.state = {
response: {},
quoteList = [];
};
}
componentDidMount() {
const socket = socketIOClient(endpoint);
let _this = this;
let {quoteList} = this.state;
socket.on("FromAPI", data => {
if(Object.keys(response).length !== 0) {
const row = symbolRowDictionary[response.ticker];
var column = 0;
switch(response.type)
{
case 'bid':
column = 1;
break;
case 'ask':
column = 3;
break;
case 'last':
column = 5;
break;
default:
console.log('Unknown type');
}
quoteList[row][column] = response.size;
quoteList[row][column + 1] = response.price;
_this.setState({ response: data, quoteList: quoteList })
}
}));
}
cellRenderer ({ columnIndex, key, rowIndex, style }) {
let {quoteList} = this.state;
return (
<div
key={key}
style={style}
>
{quoteList[rowIndex][columnIndex]}
</div>
)
}
render() {
const { response, quoteList } = this.state;
return (
<div style={{ textAlign: "center" }}>
{
<ul><li>Quote: {response.ticker} {response.type} {response.price} {response.size}</li></ul>
}
<div>
</div>
<Grid
ref={(ref) => this.theGrid = ref}
cellRenderer={this.cellRenderer}
columnCount={7}
columnWidth={75}
height={quoteList.length * 20}
rowCount={quoteList.length}
rowHeight={20}
width={800}
/>
</div>
);
}
}
export default App;
When quoteList is updated, its length will be updated accordingly and these changes will be passed to the child component Grid
EDIT
As you do not understand why does this code run, I will explan a little bit. If you don't know about React lifecycle, please take a look at this illustration
quoteList is initialized as an empty array in constructor
render is called the first time. cellRenderer will be bound to this component, or this point to App component.
After component is mounted, componentDidMount will be triggered. Here is when we should fetch data from API. Within the function block (data => {...}) , this is pointing to the function itself. So before fetching the API, I have to assign _this = this to be able to call setState.
setState is executed, render will be triggered again and since quoteList is changed. Your App will be rendered differently according to new quoteList. Means the new quoteList now is passed to your Grid. Grid also re-render.
With this flow, you don't need to forceUpdate.

After much experimentation and some online docs, I was able to solve it by putting this.theGrid.forceUpdate like this. There are no warnings, and render and rendercell are kept "pure"
componentDidMount() {
const socket = socketIOClient(endpoint);
socket.on("FromAPI", data =>
{
this.setState({ response: data });
this.theGrid.forceUpdate();
}
);
}

Related

if else statement not working in react component

I am trying to implement a condition in my react component . When the user triggers the onClick the state updates allStakes creating one array of 4 values. The problem is that I do not want the user to input more than 4 values so tried to give the limit by doing an if else statement. I tried to add a console.log in both statements.The weird fact is that setState get updated but the csonole.log is never displayed.The component keeps rendering all the values that I insert even if the array is longer than 4. Thanks in advance
import React, { Component } from 'react';
import Stake from './stake';
class FetchRandomBet extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
bet: null,
value: this.props.value,
allStakes: []
};
}
async componentDidMount() {
const url = "http://localhost:4000/";
const response = await fetch(url);
const data = await response.json();
this.setState({
loading: false,
bet: data.bets,
});
}
render() {
const { valueProp: value } = this.props;
const { bet, loading } = this.state;
const { allStakes } = this.state;
if (loading) {
return <div>loading..</div>;
}
if (!bet) {
return <div>did not get data</div>;
}
return (
< div >
{
loading || !bet ? (
<div>loading..</div>
) : value === 0 ? (
<div className="bet-list">
<ol>
<p>NAME</p>
{
bet.map(post => (
<li key={post.id}>
{post.name}
</li>
))
}
</ol>
<ul>
<p>ODDS</p>
{
bet.map(post => (
<li key={post.id}>
{post.odds[4].oddsDecimal}
<div className="stake-margin">
<Stake
onClick={(newStake) => {
if (allStakes.length <= 3) {
this.setState({ allStakes: [allStakes, ...newStake] })
console.log('stop')
} else if (allStakes.length == 4) {
console.log('more than 3')
}
}}
/>
</div>
</li>
))
}
</ul>
</div>
May be it happens because of incorrect array destructuring. Try to change this code:
this.setState({ allStakes: [allStakes, ...newStake] })
by the next one:
this.setState({ allStakes: [newStake, ...allStakes] })
Your state belongs to your FetchRandomBet component and you are trying to update that from your imported component. There are 2 solutions to that.
1> Wrap your Stake component to a separate component with onClick handler something like this
<div onClick={(newStake) => {
if (allStakes.length <= 3) {
this.setState({
allStakes: [allStakes, ...newStake
]
})
console.log('stop')
} else if (allStakes.length == 4) {
console.log('more than 3')
}
}}><Stake /></div>
Or
2> Pass the state as a prop to the Stake component which will be responsible to update the state for FetchRandomBet. something like this
<Stake parentState={this}/>
And inside the Stake component change the parentState on click of wherever you want.
I solved the problem. I transfered the onClick method in stake component and I handled the upload of the common array with an array useState. I add the value to newStake and when I click ok I retrieve newStake and spread it into a new array and then I check that array. If there is a value should not keep adding otherwise it can add values. It works fine. Thanks anyway
import React, { useState } from 'react';
import CurrencyInput from 'react-currency-input-field';
function Stake(props) {
const [newStake, setStake] = useState(null);
const [allStakes, setStakes] = useState(null);
const changeStake = (e) => {
setStake([e.target.value])
}
const mySubmit = () => {
if (!allStakes) {
setStakes([...newStake, allStakes])
props.onClick(newStake);
} else if (allStakes) {
console.log('stop')
}
}
return (
<>
<CurrencyInput
onChange={changeStake}
style={{
marginLeft: "40px",
width: "50px"
}}
placeholder="Stake"
decimalScale={2}
prefix="£"
/>
<button onClick={mySubmit}>yes</button>
<button>no</button>
{newStake}
</>
);
}
export default Stake;

How do I manage my array of children components' states?

I'm new to react, so forgive me. I'm having a problem understanding states, specifically those of children.
Purpose: I'm trying to create a form that a user can append more and more components -- in this case, images.
What happens: User appends 2 or more images. User tries to upload an image with UploadButton component, but both the images are the same. I believe this has to do with both appended children sharing the same state.
Question: How do I give each appended child its own image without affecting the other appended children?
class Page extends Component
constructor (props) {
super(props);
this.state = {
id: '',
numChildren: 0,
images: [],
}
this.onAddChild = this.onAddChild.bind(this);
}
showModal() {
this.setState({
numChildren: 0,
images: [],
});
}
renderModal()
const children = [];
//Here's my array of child components
for(var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent key={i} />);
}
return (
<ReactModal>
<this.ParentComponent addChild={this.onAddChild}>
{children}
</this.ParentComponent>
</ReactModal>
)
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
})
}
ParentComponent = (props) => (
<div>
{props.children}
<Button onClick={props.addChild}>Add Item</Button>
</div>
);
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={this.state.images}
onUploadComplete={uri => this.setState({images: uri})}
/>
</div>
);
}
Here's the code for UploadButton:
import React, { Component } from 'react';
import uuid from 'uuid';
import firebase from '../config/firebase';
class UploadButton extends Component {
constructor(props) {
super(props);
this.state = {
isUploading: false
}
}
handleClick() {
const input = document.createElement("INPUT");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/gif, image/jpeg, image/png");
input.addEventListener("change", ({target: {files: [file]}}) => this.uploadFile(file));
input.click();
}
uploadFile(file) {
console.log('F', file);
const id = uuid.v4();
this.setState({ isUploading: true })
const metadata = {
contentType: file.type
};
firebase.storage()
.ref('friends')
.child(id)
.put(file, metadata)
.then(({ downloadURL }) => {
this.setState({ isUploading: false })
console.log('Uploaded', downloadURL);
this.props.onUploadComplete(downloadURL);
})
.catch(e => this.setState({ isUploading: false }));
}
render() {
const {
props: {
value,
style = {},
className = "image-upload-button",
},
state: {
isUploading
}
} = this;
return (
<div
onClick={() => this.handleClick()}
className={className}
style={{
...style,
backgroundImage: `url("${this.props.value}")`,
}}>
{isUploading ? "UPLOADING..." : !value ? 'No image' : ''}
</div>
);
}
}
export default UploadButton;
I tried to exclude all unnecessary code not pertaining to my problem, but please, let me know if I need to show more.
EDIT: This is my attempt, it doesn't work:
//altered my children array to include a new prop
renderModal() {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent imageSelect={this.onImageSelect} key={i} />);
}
//...
};
//my attempt to assign value and pass selected image back to images array
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={uri => this.props.onImageSelect(uri)} //my greenness is really apparent here
onUploadComplete={uri => this.setState({images: uri})}
/>
//...
</div>
);
//added this function to the class
onImageSelect(uri) {
var el = this.state.images.concat(uri);
this.setState({
images: el
})
}
I know I'm not accessing the child prop correctly. This is the most complexity I've dealt with so far. Thanks for your time.
When you write this.state in Child / Parent component, you are actually accessing the state of Page. Now, I would recommend that you pass in the index of the child to the Child like so
children.push(<this.ChildComponent key={i} index={i}/>)
so that each children deals with only its own image like so
ChildComponent = ({index}) => (
<div>
<UploadButton
storage="menus"
value={this.state.images[index]}
onUploadComplete={uri => {
let images = this.state.images.slice()
images[index] = uri
this.setState({images})
}}
/>
</div>
);

It is best way filter data inside to render?

I'm new at Redux. I try to filter my code and pass to other Router component page.
Is it best way to filter my data inside render method or i should do that anywhere else? And How Can i pass my props to other router page?
I Do following;
This one a first component page.
class Home extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.actions.getProgramsStart();
}
render() {
const { ProgramsLoading, programs } = this.props.state;
if(programs) {
const SeriesFilterData=[];
const MoviesFilterData =[];
programs.map(FilterPrograms => {
if(FilterPrograms.programType==="series" && FilterPrograms.releaseYear >= 2010){
SeriesFilterData.push(FilterPrograms);
}
if(FilterPrograms.programType==="movie" && FilterPrograms.releaseYear >= 2010){
MoviesFilterData.push(FilterPrograms);
}
});
}
return (
<div id="home">
{ ProgramsLoading ? <div><Loader style={{ display: "block" }} content="Program List loading" /></div> : <h1>program data</h1> }
</div>
);
}
}
const mapStateToProps = (state) => {
return {
state: {
...state.home
}
};
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(homeActions, dispatch)
};
};
Yes you can avoid entering the filters by returning something previously based on your ProgramsLoading, and then change your map which is returning an empty array and creating 2 additional arrays in each render for a reduce which will use an object with just the 2 arrays that you need, everything in 1 loop.
Also take into account that you call FilterPrograms to the variable of your map, and it is confusing, because it is the current program and FilterPrograms sounds more like a function instead.
class Home extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.actions.getProgramsStart();
}
render() {
const { ProgramsLoading, programs } = this.props.state;
//check if you are loading, so you dont need to apply filters or whatever you add (filter/map creates a new array each time)
if(ProgramsLoading) return <div><Loader style={{ display: "block" }} content="Program List loading" /></div>
const defaultValue = {SeriesFilterData: [], MoviesFilterData =[]}
const reducerFunction = (accum, currentValue)=>{
//if this check is an AND for all the cases, return directly if the item doesnt match.
if(currentValue.releaseYear < 2010) return accum;
if(currentValue.programType==="series"){
accum.SeriesFilterData.push(currentValue);
} else if(currentValue.programType==="movie"){
accum.MoviesFilterData.push(currentValue);
}
return accum;
}
const results = programs.reduce( reducerFunction, defaultValue);
// using {...result} will destructure to be (SeriesFilterData, MoviesFilterData) separeted props
return (
<div id="home">
<h1>program data</h1>
<SomeComponent {...resulst} />
</div>
);
}
}

How can I pass the state from parent component to a child to use in an API call?

I have a React app that queries an API that I built in Spring Boot. The React side should query the database to find a number of Activity objects to populate one Itinerary object. I want to keep track of the Ids of the activities so the app doesn’t return the same one more than once. Here’s how I set it up:
The Itinerary component has an array called "activities" in its state that will hold the ids. This starts out as empty. The Itinerary component also has a function called UpdateActivities() that updates the State with a new id when an activity is added.
UpdateActivities is passed as a callback to the child component Activity as props. The activities array in the state is turned into a string (because I'll be using this in the API call) and also passed as props to the Activity component.
In the Activity component, the callback function is further passed as a prop to the ActivityDetails component and the string of ids is further passed to ActivityDetails as well.
The ActivityDetails component makes the actual API call to my Spring Boot application and uses the callback function to update the State on the parent component. I attempted to save the string of ids to a constant to use in my API call. I want the string to go at the end of the call, so my app will know to skip over any activities with those Ids (currently hard-coded to 1).
The API call doesn't work if I replace 1 with the constant string of ids, because it shows up as empty. Is this an async issue? Is there a way to make it so the React app doesn't make the API call on ActivityDetails until the State is updated on the parent component?
Itinerary Component
class Itinerary extends Component {
constructor(props) {
super(props);
this.state = {
template: null,
activities: []
};
}
updateActivities = (id) => {
const activityList = [...this.state.activities, id];
this.setState({
activities: activityList
})
}
componentDidMount = () => {
const {duration, travelerType, pace} = this.props.userAnswers;
const transport = this.props.userAnswers.internalTravel.sort().join(', ');
const TEMPLATES_URL = `http://localhost:8080/templates/duration/${duration}/travelers/${travelerType}/pace/${pace}/transport/${transport}`;
axios.get(TEMPLATES_URL)
.then(response => {
this.setState({
template: response.data[0].content
});
})
.catch(function (error) {
console.log(error.message);
});
}
render() {
let cities;
if (this.state.template === null) {
cities = <div><h3>Loading...</h3></div>;
} else {
let data = this.state.template.split(", ");
cities = data.map((city, index) => {
return (
<section>
<City
key={index}
day={index + 1}
city={city}
/>
<Activity
key={`${city}${index}`}
day={index + 1}
lastDay={data.length}
city={city}
userAnswers={this.props.userAnswers}
updateActivityState={this.updateActivities}
activityList={this.state.activities.join(', ')}
/>
</section>
)
});
}
return (
<div>
<div className="row">
<div className="col s9">
<h3>{cities}</h3>
</div>
</div>
</div>
);
}
}
export default Itinerary;
Activity Component
class Activity extends Component {
render() {
let city = this.props.city;
if(this.props.day === 1) {
return (
<ActivityDetails
userAnswers={this.props.userAnswers}
city={city}
timeOfDay="evening"
handleActivityList={this.props.updateActivityState}
activities={this.props.activityList}/>
);
} else if (this.props.day === this.props.lastDay) {
return (
<ActivityDetails
userAnswers={this.props.userAnswers}
city={city} timeOfDay="morning"
handleActivityList={this.props.updateActivityState}
activities={this.props.activityList} />
);
} else {
return(
<section>
<ActivityDetails
userAnswers={this.props.userAnswers}
city={city}
timeOfDay="morning"
handleActivityList={this.props.updateActivityState}
activities={this.props.activityList} />
<ActivityDetails
userAnswers={this.props.userAnswers}
city={city}
timeOfDay="afternoon"
handleActivityList={this.props.updateActivityState}
activities={this.props.activityList} />
<ActivityDetails
userAnswers={this.props.userAnswers}
city={city}
timeOfDay="evening"
handleActivityList={this.props.updateActivityState}
activities={this.props.activityList} />
</section>
);
}
}
}
export default Activity;
ActivityDetails Component
class ActivityDetails extends Component {
constructor(props) {
super(props);
this.state = {
activity: 'Loading...',
};
}
componentDidMount() {
const {travelParty, budget, pace, sites} = this.props.userAnswers;
const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
const interests = this.props.userAnswers.interests.sort().join(', ');
const entertainment = this.props.userAnswers.entertainment.sort().join(', ');
const currentActivities = this.props.activities;
console.log(`currentActivities: ${currentActivities}`);
const city = this.props.city;
const timeOfDay = this.props.timeOfDay;
const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/1`;
console.log(ACTIVITY_URL);
axios.get(ACTIVITY_URL)
.then(response => {
const newActivity = response.data.content;
const updatedActivityId = response.data.id;
this.props.handleActivityList(updatedActivityId);
this.setState({
activity: newActivity,
});
})
.catch(function(error) {
console.log(error.message);
});
}
render () {
return (
<div>{this.state.activity}</div>
);
}
}
export default ActivityDetails;
Revised with ComponentDidMount:
class ActivityDetails extends Component {
constructor(props) {
super(props);
this.state = {
activity: 'Loading...',
};
}
getActivity() {
const {travelParty, budget, pace, sites} = this.props.userAnswers;
const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
const interests = this.props.userAnswers.interests.sort().join(', ');
const entertainment = this.props.userAnswers.entertainment.sort().join(', ');
const city = this.props.city;
const timeOfDay = this.props.timeOfDay;
const currentActivities = this.props.activities;
console.log(`currentActivities: ${currentActivities}`);
const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/${currentActivities}`;
console.log(ACTIVITY_URL);
axios.get(ACTIVITY_URL)
.then(response => {
const newActivity = response.data.content;
const updatedActivityId = response.data.id;
this.props.handleActivityList(updatedActivityId);
this.setState({activity: newActivity});
})
.catch(function(error) {
console.log(error.message);
});
}
componentDidMount() {
this.getActivity();
}
componentDidUpdate(prevProps) {
if(prevProps.activities !== this.props.activities) {
this.getActivity();
}
}
render () {
return (
<div>{this.state.activity}</div>
);
}
}
export default ActivityDetails;
The API call doesn't work if I replace 1 with the constant string of ids, because it shows up as empty. Is this an async issue?
This makes sense, yes. It's likely doing your Itinerary's fetch at roughly the same time as your ActivityDetails' fetch, so the state.activities there is still an empty array (ergo empty string once you've mutated it and passed it down).
Is there a way to make it so the React app doesn't make the API call on ActivityDetails until the State is updated on the parent component?
Yes, you can use other lifecycle methods to facilitate this. In your case, you probably want componentDidUpdate.
You could do something like:
class ActivityDetails extends Component {
constructor(props) {
super(props);
this.state = {
activity: 'Loading...',
};
}
doMyGet(values) {
const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/1`;
console.log(ACTIVITY_URL);
axios.get(ACTIVITY_URL)
.then(response => {
const newActivity = response.data.content;
const updatedActivityId = response.data.id;
this.props.handleActivityList(updatedActivityId);
this.setState({
activity: newActivity,
});
})
.catch(function(error) {
console.log(error.message);
});
}
componentDidMount() {
const {travelParty, budget, pace, sites} = this.props.userAnswers;
const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
const interests = this.props.userAnswers.interests.sort().join(', ');
const entertainment = this.props.userAnswers.entertainment.sort().join(', ');
const currentActivities = this.props.activities;
console.log(`currentActivities: ${currentActivities}`);
const city = this.props.city;
const timeOfDay = this.props.timeOfDay;
this.doMyGet(currentActivities)
}
componentDidUpdate(prevProps) {
if (this.props.activities !== prevProps.activities) {
this.doMyGet(this.props.activities)
}
}
render () {
return (
<div>{this.state.activity}</div>
);
}
}
export default ActivityDetails;

Trying to manipulate a div with reactjs on async data

I try to animate a div with reactjs using async data via redux and it's not clear to me when can I have a reference to the virtual dom on state loaded.
In my case I have a div with id header where I would like to push down the container when data was populated.
If I try in componentDidMount than I get Cannot read property 'style' of undefined because componentDidMount still having a reference to an on load container
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
sliderLength: null
}
}
componentDidMount() {
this.props.actions.getSlides()
if(this.header) {
setTimeout(function() {
this.header.style.bottom = -(this.header.clientHeight - 40) + 'px';
}, 2000);
}
//header.style.bottom = -pushBottom+'px';
}
componentWillReceiveProps(nextProps) {
let {loaded} = nextProps
if(loaded === true ) {
this.animateHeader()
}
}
animateHeader() {
}
componentWillMount() {
const {slides} = this.props;
this.setState({
sliderLength: slides.length,
slides: slides
});
}
render() {
const {slides, post, loaded} = this.props;
if(loaded ===true ) {
let sliderTeaser = _.map(slides, function (slide) {
if(slide.status === 'publish') {
return <Link key={slide.id} to={'portfolio/' + slide.slug}><img key={slide.id} className="Img__Teaser" src={slide.featured_image_url.full} /></Link>
}
});
let about = _.map(post, function (data) {
return data.content.rendered;
})
return (
<div className="homePage">
<Slider columns={1} autoplay={true} post={post} slides={slides} />
<div id="header" ref={ (header) => this.header = header}>
<div className="title">Title</div>
<div className="text-content">
<div dangerouslySetInnerHTML={createMarkup(about)}/>
</div>
<div className="sliderTeaser">
{sliderTeaser}
</div>
<div className="columns">
<div className="column"></div>
<div className="column"></div>
<div className="column"></div>
</div>
</div>
<div id="bgHover"></div>
</div>
);
} else {
return <div>...Loading</div>
}
}
}
function mapStateToProps(state) {
return {
slides: state.slides,
post: state.post,
loaded: state.loaded
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(slidesActions, dispatch)
};
}
function createMarkup(markup) {
return {__html: markup};
}
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
How do I deal in this case with states?
Between I found a solution but not sure if is the right workaround
componentDidUpdate() {
if(this.header) {
setTimeout(function() {
this.header.style.bottom = -(this.header.clientHeight - 35) + 'px';
}, 2000);
}
}
In general, try to avoid using ref as much as possible. This is particularly difficult if you're new to React but with some training, you'll find yourself not needing it.
The problem with modifying the styles like you're doing is that when the component will render again your changes will be overwritten.
I would create a new state property, say state.isHeaderOpen. In your render method you will render the header differently depending on the value of this header e.g.:
render () {
const {isHeaderOpen} = this.state
return (
<header style={{bottom: isHeaderOpen ? 0 : 'calc(100% - 40px)'}}>
)
}
Here I'm using calc with percentage values to get the full height of the header.
Next, in your componentDidMount simply update the state:
componentDidMount () {
setTimeout(() => this.setState({isHeaderOpen: false}), 2000);
}
In this way, the component will render again but with the updated style.
Another way is to check if the data has been loaded instead of creating a new state value. For example, say you're loading a list of users, in render you would write const isHeaderOpen = this.state.users != null.
If you are trying to animate a div why are you trying to access it by this.header just use the javaScript's plain old document.getElementById('header') and then you can play around with the div.

Resources