In my project I have some components, one of them is a div which consists several other components and is a button and its use is like a menu.
Trying to make a loading page overlayed to prevent actions during the charge of components, I modified my code:
1) Including in the constructor:
this.state = {
isLoaded: false
}
2) Adding a componentDidMount() method:
componentDidMount() {
this.setState({
isLoaded: true
})
}
3) Changing the render method:
const isLoaded = this.state.isLoaded;
if (!isLoaded) {
document.getElementById("loadingPageHTML").style.display = "inline-block";
return(
<p className = "noShowIt"> Hola </p>
);
} else {
document.getElementById("loadingPageHTML").style.display = "none";
return (
<div className="MainMenuButtons">
...
}
But when I debug on my browser I see that when I press one of these buttons, the flow is always enter in the render method of this component, so, the loading page display is always inline-block status even if the second component (with the same state and conditions) isn't loaded.
What's the way to develop a loading lay with a good behaviour?
Is it possible to do it in a similar way as ajaxStart function in jQuery?
You're kind of mixing react components with an older style of web development.
You'd want to render a loading component or your content components based on the isLoaded state rather then using css and dom selectors. for example:
render () => (!isLoaded) ? <LoadingComponent /> : <Content />;
render() {
if (!isLoaded) {
return <LoadingComponent />;
} else {
return (<div> ... </div>);
}
}
When the state changes the components being rendered will automatically update
Related
I need to know when Hidden Element did mount.
I use ref to check it did mount and control this element.
And use componentDidUpdate to check when Hidden Element did mount.
But use componentDidUpdate in a big project, some elements often trigger componentDidUpdate.
I'm afraid the efficiency will be bad.
Is there another way for me to know when Hidden Element did mount?
Appreciate your help.
In addition, why I need to know it because I need to use a Radium package to build the animation.
When the 'someState' is true, I will auto play the animation for the element.
I use the style animation-play-state : 'running'.
This animation will break in the safari, but it is okay that users visit it for the first time.
When the users refresh safari and have a cache, the users visit it again causing the animation can't autoplay.
So I set animation-play-state : paused.
When I confirm the element did mount, I will use ref change animation-play-state to running.
I find an issue with this problem.
Link: https://github.com/FormidableLabs/radium/issues/912
My sudo code.
import React from "react";
const initialState = {
someState: false
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.hiddenElement = null;
}
componentDidMount() {
console.log("componentDidMount");
}
componentDidUpdate() {
console.log("componentDidUpdate");
if (this.hiddenElement !== null) console.log("hiddenElement did Mount");
// I will change the properties of this.hiddenElement, or others in the next steps.
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>
Click Me
</button>
{someState && (
<div ref={r => (this.hiddenElement = r)}>Hidden Element</div>
)}
</div>
);
}
}
export default App;
Okay, so if you're using an external package and need to manipulate the element based on a state change, then you'll have to basically use componentDidUpdate().
Only worry about the performance when it becomes a bottleneck (after profiling things, etc.).
Since setting a ref doesn't cause componentDidUpdate (it's not a bit of state), you may wish to refactor the animation-mutating method to something like this (note how the update...() method is called in the ref callback).
class App extends React.Component {
constructor(props) {
super(props);
this.state = { someState: false };
this.hiddenElement = null;
}
updateElementAnimation() {
if (!this.hiddenElement) return; // not mounted yet
if (this.state.someState) {
this.hiddenElement.something();
} else {
this.hiddenElement.somethingElse();
}
}
componentDidUpdate() {
this.updateElementAnimation();
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>Click Me</button>
{someState && (
<div
ref={r => {
this.hiddenElement = r;
this.updateElementAnimation();
}}
>
Hidden Element
</div>
)}
</div>
);
}
}
You can check inside your componentDidUpdate the value of your someState. If this is true, then you are sure that the element you need is rendered, as componentDidUpdate is invoked after an update occurs (thus after the render method).
...
componentDidUpdate() {
if (this.state.someState) {
// your element is rendered, do what you need
}
}
...
Currently have a NextJS site with several pages. Each page can have a number of components in any order. These components are driven by a CMS by whatever the end user chooses.
Example, they could have a carousel, followed by a grid, followed by paragraph component.
Each page I get initial props, which will have the list of components for that page, and the data for each component.
Page1.getInitialProps = async () => {
const res = await fetch('some api')
const json = await res.json()
return { data: json }
}
This ultimately reaches a top level parent component that looks at the data and renders the appropriate child component on the page with a switch statement
renderWidgets = (data) => {
const pageWidgets = data.widgets.map((widget) => {
switch(widget.type) {
case WIDGETS.CAROUSEL:
return <Carousel />;
case WIDGETS.GRID:
return <Grid />;
case WIDGETS.PARAGRAPH:
return <Paragraph />;
default:
return <div />;
}
});
return pageWidgets;
}
render() {
return (
<div className="layout__main">
{this.renderWidgets()}
</div>
);
}
My question is if there is a better way to handle this this rendering component process. Each time a brand new widget is created I'll have to make sure to update the switch statement. Is there a better way of rendering dynamic CMS driven components on a page?
I've got a conditional that displays an editor while a certain prop remains true. The thing is, the data with which that editor is rendered with should change every time I select another object with which to populate that editor.
However, because the prop responsible for the conditional rendering doesn't change, even though the data with which the editor is rendered does, it refuses to re-render on state change.
I'm not particularly good at React, so, hopefully someone can explain how I can get around this little hiccup.
Conditional render
{this.state.showEditor ? (<BlockEditor routine={this.state.editorObject} />) : null}
Method that is being called.
handleShowEditor = routine => {
this.setState({ showEditor: true });
this.setState({ editorObject: routine });
};
The editor component
export default class BlockEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
routine: this.props.routine
};
}
render() {
return (
<div>
<Editor
autofocus
holderId="editorjs-container"
onChange={data => this.handleSave(data)}
customTools={{}}
onReady={() => console.log("Start!")}
data={this.props.routine.description}
instanceRef={instance => (this.editorInstance = instance)}
/>
</div>
);
}
}
Is there a reason for setting state separately? Why not
handleShowEditor = routine => {
this.setState({
showEditor: true,
editorObject: routine
});
};
Keep in mind that setState is asynchronous and your implementation could lead to such weird behaviour.
If you are still looking for an answer i have faced the same problem working with the same [Editor.JS][1] :).
This worked for me with functional component:
// on change fires when component re-intialize
onChange={async (e) => {
const newData = await e.saver.save();
setEditorData((prevData) => {
console.log(prevData.blocks);
console.log(newData.blocks);
if (
JSON.stringify(prevData.blocks) === JSON.stringify(newData.blocks)
) {
console.log("no data changed");
return prevData;
} else {
console.log("data changed");
return newData;
}
});
}}
// setting true to re-render when currentPage data change
enableReInitialize={true}
Here we are just checking if data changes assign it to editorData component state and perform re-render else assign prevData as it is which will not cause re-render.
Hope it helps.
Edit:
i am comparing editor data blocks change which is array.
of course you need to perform comparison of blocks more deeply than what i am doing, you can use lodash for example.
[1]: https://github.com/editor-js/awesome-editorjs
As setState is asynchronous you can make another call in its callback.
Try like this
handleShowEditor = routine => {
this.setState({
showEditor: true
}, () =>{
this.setState({
editorObject: routine
)}
});
};
I am working to build a Pokedex from JSON data in React. I am refactoring this project from one I built in jQuery, so it could be that the jQuery approach is causing me to misunderstand how to approach this problem with proper React thinking. What's tripping me up so far is how to dynamically render multiple child elements based on the JSON I pass from a the parent element (this would be jQuery append).
Here is my App.js code:
class App extends Component {
render() {
return (
<div className="App background">
<div className="content">
<Header />
<TilesContainer pokedexName="national"/>
</div>
</div>
);
}
The TilesContainer essentially receives the name of a Pokedex and makes a call to an API. The individual Pokemon names are stored in an array in the TilesContainer state (this.state.pokemon), as below.
class TilesContainer extends Component {
constructor(props){
super(props);
this.state = {pokemon: []};
this.getPokemon = this.getPokemon.bind(this);
this.tiles = this.tiles.bind(this);
}
getPokemon() {
// set this.state.pokemon to the list
let link = 'https://pokeapi.co/api/v2/pokedex/' + this.props.pokedexName + '/';
fetch(link)
.then(response => response.json())
.then(myJson => {
let list = myJson['pokemon_entries'];
list.forEach(pokemon => {
this.state.pokemon.push(pokemon);
})
})
this.tiles();
}
tiles() {
if (this.state.pokemon.length > 0) {
return (
this.state.pokemon.map(pokemon => {
<Tile number={pokemon.entry_number}/>
})
)
}
}
render(){
this.getPokemon();
return (
<div id="tiles-container"
className="tiles-container">
<h1>TilesContainer Test</h1>
<Tile number={1} />
</div>
)
}
}
export default TilesContainer
Again, the idea is that a Pokemon tile is render for each Pokemon in the Pokedex JSON (which for now I've stored in this.state.pokemon - not sure if this is the best approach). I found an example here on Stack Overflow that uses an additional function (this this case this.tiles() to generate what I think is an array of returns with different child elements). The <Tile number={1} /> is a hardcoded example of how the tile is called.
Currently no dynamically-rendered tiles show up when the code runs. Is this the correct approach. I'd really appreciate any suggestions.
Thanks!
It looks like you're almost there.
First off, never modify state directly. Use this.setState() instead. State in React is updated asynchronously. For your purposes, you should be able to modify getPokemon() like the following. I also removed the this.tiles() call, as it is unnecessary.
getPokemon() {
// set this.state.pokemon to the list
let link = 'https://pokeapi.co/api/v2/pokedex/' + this.props.pokedexName + '/';
fetch(link)
.then(response => response.json())
.then(myJson => {
let list = myJson['pokemon_entries'];
this.setState({
pokemon: list,
});
})
}
A minor correction for tiles(): when using an arrow function and returning something in one line, use parentheses instead of curly braces. When you use curly braces, you have to include a return statement. With parentheses, you do not.
tiles() {
if (this.state.pokemon.length > 0) {
return (
this.state.pokemon.map(pokemon => (
<Tile number={pokemon.entry_number}/>
))
)
}
}
Next, since tiles() returns your dynamic tile components, it needs to be included in what you return in render().
render(){
return (
<div id="tiles-container"
className="tiles-container"
>
<h1>TilesContainer Test</h1>
{this.tiles()}
</div>
)
}
Lastly, I think the call to this.getPokemon() would make more sense in the constructor, rather than in render().
I think your method of getting the json data and storing it in state is fine, by the way. In the future, you may want to look into Redux to manage your state, but it could be overkill for a really small application.
so you are passing the pokedexName from the parent component which is app.js, once you get the props you can call the rest api call on the componentWillMount life cycle.
so on the render since the api call has been initiated it wont have any data thats why we are using a ternary operator to check the array once the api call get finished and we get the data we are setting the data to the pokemon array.
Since the state is updated react will automatically render a re render so the data will appear.
i hope the below code will solve the issue, please let me know :)
// App.js
import React, { Component } from 'react';
import TilesContainer from './components/TileContainer/TilesContainer'
class App extends Component {
render() {
return (
<div>
<TilesContainer pokedexName="national" />
</div>
);
}
}
export default App;
// Tiles container
import React, {Component} from 'react';
import axios from 'axios';
class TilesContainer extends Component{
//state
state ={
pokemon: []
}
// life cycle methods
componentWillMount(){
let link = 'https://pokeapi.co/api/v2/pokedex/' + this.props.pokedexName + '/';
axios.get(link)
.then(res => {
this.setState({
pokemon: res.data["pokemon_entries"]
})
})
}
render(){
let style ={display:"inline"}
return(
<div>
{
this.state.pokemon.length > 0 ?
this.state.pokemon.map(pokemon => {
return(
<div key={pokemon.entry_number}>
<p style={style}>{pokemon.entry_number}</p>
<a href={pokemon.pokemon_species.url}>{pokemon.pokemon_species.name}</a>
</div>
)
})
:
null
}
</div>
)
}
}
export default TilesContainer
I'm trying to do Step 15 of this ReactJS tutorial: React.js Introduction For People Who Know Just Enough jQuery To Get By
The author recommends the following:
overflowAlert: function() {
if (this.remainingCharacters() < 0) {
return (
<div className="alert alert-warning">
<strong>Oops! Too Long:</strong>
</div>
);
} else {
return "";
}
},
render() {
...
{ this.overflowAlert() }
...
}
I tried doing the following (which looks alright to me):
// initialized "warnText" inside "getInitialState"
overflowAlert: function() {
if (this.remainingCharacters() < 0) {
this.setState({ warnText: "Oops! Too Long:" });
} else {
this.setState({ warnText: "" });
}
},
render() {
...
{ this.overflowAlert() }
<div>{this.state.warnText}</div>
...
}
And I received the following error in the console in Chrome Dev Tools:
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.
Here's a JSbin demo. Why won't my solution work and what does this error mean?
Your solution does not work because it doesn't make sense logically. The error you receive may be a bit vague, so let me break it down. The first line states:
Cannot update during an existing state transition (such as within render or another component's constructor).
Whenever a React Component's state is updated, the component is rerendered to the DOM. In this case, there's an error because you are attempting to call overflowAlert inside render, which calls setState. That means you are attempting to update state in render which will in then call render and overflowAlert and update state and call render again, etc. leading to an infinite loop. The error is telling you that you are trying to update state as a consequence of updating state in the first place, leading to a loop. This is why this is not allowed.
Instead, take another approach and remember what you're trying to accomplish. Are you attempting to give a warning to the user when they input text? If that's the case, set overflowAlert as an event handler of an input. That way, state will be updated when an input event happens, and the component will be rerendered.
Make sure you are using proper expression. For example, using:
<View onPress={this.props.navigation.navigate('Page1')} />
is different with
<View onPress={ () => this.props.navigation.navigate('Page1')} />
or
<View onPress={ () => {
this.props.navigation.navigate('Page1')
}} />
The two last above are function expression, the first one is not. Make sure you are passing function object to function expression () => {}
Instead of doing any task related to component in render method do it after the update of component
In this case moving from Splash screen to another screen is done only after the componentDidMount method call.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button,
Image,
} from 'react-native';
let timeoutid;
export default class Splash extends Component {
static navigationOptions = {
navbarHidden: true,
tabBarHidden: true,
};
constructor(props) {
super(props)
this.state = { navigatenow: false };
}
componentDidMount() {
timeoutid=setTimeout(() => {
this.setState({ navigatenow: true });
}, 5000);
}
componentWillUnmount(){
clearTimeout(timeoutid);
}
componentDidUpdate(){
const { navigate,goBack } = this.props.navigation;
if (this.state.navigatenow == true) {
navigate('Main');
}
}
render() {
//instead of writing this code in render write this code in
componenetDidUdpate method
/* const { navigate,goBack } = this.props.navigation;
if (this.state.navigatenow == true) {
navigate('Main');
}*/
return (
<Image style={{
flex: 1, width: null,
height: null,
resizeMode: 'cover'
}} source={require('./login.png')}>
</Image>
);
}
}
Call the component props at each time as new render activity. Warning occurred while overflow the single render.
instead of
<Item onPress = { props.navigation.toggleDrawer() } />
try like
<Item onPress = {() => props.navigation.toggleDrawer() } />
You can also define the function overflowAlert: function() as a variable like so and it will not be called immediately in render
overflowAlert = ()=>{//.....//}