I'm using React with Redux.
In this example I have my class with mapStateToProps and mapDispatchToProps
class EnigmaPage extends Component {
constructor(props){
super(props);
}
componentDidMount() {
this.props.authCheckState();
}
readUserData() {
this.props.loadLevel(this.props.userId);
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
authCheckState: () => dispatch(actions.authCheckState()),
getLevel: () => dispatch(actions.getLevel()),
loadLevel:(id) => dispatch(actions.loadLevel(id))
};
}
const mapStateToProps = state => {
return {
userId:state.auth.user,
level:state.level.level
}
}
I wanna push to my component LevelInfo the values difficulty and level but these 2 data arrive from getLevel() that is an http request with delay.
The page loads before receiving all the data from the http call.
I'm looking a way to wait to load the component LevelInfo or reload the component when the data are all ready.
You need to tell your component that will wait for the data needed to render your Level component, so into your EnigmaPage component write as follow
render(){
const { level } = this.props;
if (!level) { return <LoadingComponentSpinner />; } // this will render only when level attr is not set, otherwise will render your `LevelInfo` component
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
I hope it can help you.
We don't make our components wait, we let the initial rendering happens but render the target component with a conditional expression.
render() {
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.props.level && (
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
)}
</div>
</div>
</div>
);
}
So, here we are checking if this.props.level is defined. When it is undefined React does not render anything, after getting the data LevelInfo component is rendered. You can change conditional rendering logic. You can render a Loading component maybe instead of nothing. But, at the end of the day, you will render your component conditionally.
try to use conditional rendering:
isDataReady() {
if(this.props.level.difficulty && this.props.level.level)
return true;
return false;
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.isDataReady() ? <LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
: <div> </div>}
</div>
</div>
</div>
);
}
in case your data is not ready you can display anything you want, for simplicity here I just added an empty <div>.
Johuder Gonzalez has talked about the Spinner. The Material UI has a lot of examples, which is easy to apply. Please look the followings.
Material UI Progress
Related
how to pass the variable "devscreens" from loginform to app.js on the below criteria and move to the next screen in react js.
Loginform.js:
async doLogin(){
this.setState({invalid_user:false});
this.setState({welcome_user:false});
this.setState({devscreens:false});
if (!this.state.username){
return;
}
this.setState({
buttonDisabled: true
})
try{
const response = await axios.get(`${loginidURL}`)
if(response.status === 200)
{ this.setState({welcome_user:true});
console.log(response.data.username)
console.log(response.data)
console.log(response.data.userlevel)
if (response.data.userlevel == 11 )
{
this.setState({devscreens:true});
console.log(this.state.devscreens)
}
if (response.data.userlevel == 13 )
{
this.setState({devscreens:false});
}
userid=response.data.username
this.resetForm();
}
}
App.js:
function App() {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<div className="app">
<div className='container'>
<h1>Application </h1>
<h3>App</h3>
<LoginForm/>
</div>
</div>
<div className={classes.appMain}>
<Header />
<Welcome username=""/>
<Board />
</div>
I am new to react working in logic screen and its next screen integration.
Wanted to pass the devscreen value from Loginform.js to App.js. and if devscreen is true then i need to execute the below in app.js in an separate screen.
<Header />
<Welcome username="Jo"/>
<Board />
</div>
Can some one help me with this.I tried using props but that did not work for me..
A common flow in React is that the App.js component contains all other components and logic within it. So the component is a container for all other components.
The logic is to then decide which child components to render depending on the state of parent components. You can pass objects to child components using props, which then help decide which pieces to render.
Please have a look through the examples here:
https://reactjs.org/tutorial/tutorial.html
To keep it simple, the detail page fetches data on mount based on the movie ID in the URL, this coming from path='movie/:id' in the Route.
It's child is called Recommended, which shows you recommended movies based again on the current URL.
class MovieDetailPage extends React.Component {
// Fetch movies and cast based on the ID in the url
componentDidMount() {
this.props.getMovieDetails(this.props.match.params.id)
this.props.getMovieCast(this.props.match.params.id)
}
render() {
<div>
Movies here
</div>
<Recommended id={this.props.match.params.id}/>
}
}
The Recommended component fetches data based on the current movie as well and generates another tag pointing to another movie.
class Recommended extends React.Component {
componentDidMount() {
this.props.getRecommended(this.props.id)
}
render() {
return (
<>
<Category title={'Recommended'}></Category>
<div className="movies">
{
this.props.recommended.map((movie) => {
return (
<Link key={movie.id} to={`movie/${movie.id}`} className="movies__item">
<img
key={movie.id}
src={`https://image.tmdb.org/t/p/w342${movie.poster_path}`}
className="movies__item-img"
alt={`A poster of ${movie.title}`}
>
</img>
</Link>
)
})
}
</div>
</>
)
}
}
Now how can I trigger another render of the parent component when clicking the Link generated in the Recommended component? The URL is changing but this won't trigger a render like I intent to do.
UPDATE:
<Route
path="/movie/:id"
render={(props) => (
<MovieDetailPage key={props.match.params.id}
{...props}
)}
/>
I passed in a unique key this time that triggered the re-render of the page. I tried this before but I might've screwed up the syntax.
This post got me in the right direction: Force remount component when click on the same react router Link multiple times
Add a key to the page
If you change route but your page is not getting its "mount" data then you should add a key to the page. This will cause your page to rerender and mount with the new id and get the data again.
You can read more about react keys here
A key tells react that this is a particular component, this is why you see them in on lists. By changing the key on your page you tell react that this is a new instantiation of the component and has changed. This will cause a remount.
Class component example
class MyPage extends React.Component {
componentDidMound() {
// this will fire each time the key changes since it triggers a mount
}
render() {
return (
<div key={props.pageId}>
{/* component stuff */}
</div>
)
}
}
Functional component example
const MyPage = (props) => {
React.useEffect(() => {
// this will fire each time the key changes
}, []);
return (
<div key={props.pageId}>
{/* component stuff */}
</div>
)
}
You can add another React lifecycle method that triggers on receiving new props (UNSAFE_componentWillReceiveProps, componentDidUpdate, getDerivedStateFromProps) in your Recommended component like this:
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
nextProps.getRecommended(nextProps.id);
};
}
You can also add key to your component (which forces it to re-render completely if key changed) like this:
<Recommended key={this.props.match.params.id} id={this.props.match.params.id}/>
You can also use React Hooks to handle this more easily with useEffect:
const Recommended = (props) => {
const { id, getRecommended, recommended } = props;
useEffect(() => {
id && getRecommended(id);
}, [id]);
return (
<>
<Category title={'Recommended'}></Category>
<div className="movies">
{recommended.map((movie) => {
return (
<Link key={movie.id} to={`movie/${movie.id}`} className="movies__item">
<img
key={movie.id}
src={`https://image.tmdb.org/t/p/w342${movie.poster_path}`}
className="movies__item-img"
alt={`A poster of ${movie.title}`}
></img>
</Link>
);
})}
</div>
</>
);
};
Note: adding key to component and complete its re-render is not best practice and you should be using Component's lifecycles to avoid it if possible
I am getting data from a WebAPI call in my componentDidMount on my parent react component. I put the values into state.
When I render my form, I am just making custom labels with data (A component), and passing the label text, and the data to this component. (One for each field I am displaying). I pass the values from state, via props, to the child components.
But what I am finding is that my child components are rendering without data being populated... as this seems to happen before the api call happens. The api happens, the state gets set, but data never gets to the child components. I thought that the props would pass the updated state data to the components. I'm wrong. How should I achieve this? I want to load the data in my parent, and then render the child components, passing in the data.
componentDidMount() {
this.loadData();
}
loadData() {
var request = {
method: 'GET',
URL: "http://example.com/api/user/profile",
}
fetchData(request).then(response => {
if(response.errorcode != "OK")
{
console.log("Bad response from API. Need to redirect!")
}
else
{
this.setState(
{
firstname: response.payload.firstname,
surname: response.payload.surname,
email: response.payload.email,
countryId: response.payload.countryId,
countries: response.payload.countries
}
);
}
});
}
render() {
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
My display label component is simply this. I'm new to this, and just trying to make reusable components:
import React, {Component} from 'react';
export default class DisplayLabel extends Component {
constructor(props)
{
super(props);
this.state = {
labelText: this.props.labelText,
data: this.props.data
}
console.log(this.state);
}
componentDidMount() {
this.setState({
labelText: this.props.labelText,
data: this.props.data
});
console.log("ComponentDidMount", this.state);
}
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.state.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.state.data}</span></strong>
</div>
</div>
</div>
)
}
}
I need to wait for the API call to complete before I render the form?
This is a common problem in React. I usually try to resolve it with a pattern that shows some sort of loading indicator. So I would initialize your state like this in the constructor:
this.state = {
loading: true
}
And I would change your render to have a check for that loading bool:
render() {
if (this.state.loading) {
return (<h1>Loading, please wait...</h1>);
}
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
Then you can set loading to false following a successful data pull, and your form will display correctly without any errors.
(I edited this to use your parent component rather than DisplayLabel, as it makes more sense there. However, the component name is omitted from your question).
I'm not sure you have to wait until the request complete (but you most likely do have to for UI issues), but i guess your problem is something else.
you took the props and included them inside your state (that's unneccery), and then when the component complete it's loading you update the state again, but the request probbaly compleate after the componnent complete it's loading.
if you must to put the props inside your state, you should add componentDidUpdate or componentWillReceiveNewProps lifecycle functions, not the componentDidMount.
but, if you don't have to (most likley), you might change your render funcion to:
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.props.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.props.data}</span></strong>
</div>
</div>
</div>
)
}
(just changed the state to props)
Try that, Good Luck!
Just because constructor called once when component mount so you can do one thing, in your render take two var and assign data into that. And then use those variable to show data. Because child component will render Everytime state of parent will change but constructor will not.
Im looking into higher order functions and i dont really understand how this part works.
say i have the following function:
const withAdminWarning = WrappedComponent => {
return props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<WrappedComponent {...props} />
</div>
);
};
const Info = props => (
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
);
const AdminInfo = withAdminWarning(Info);
ReactDOM.render(
<AdminInfo isAdmin={true} info="There are the details" />,
document.getElementById("app")
);
From my understanding of components, to access the props variable, you have to use either props, if its a stateless component, or this.props if it is a class component.
From where does the props come into play in the example above as i cant get access to it from the WrappedComponent or anywhere else apart from the return statement.
The Higher order Component returns a function which is a functional component. Am I right in thinking that foo(Info) means withAdminWarning(Info)?
So after calling withAdminInfo the AdminInfo Component looks basically like:
const AdminInfo = props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
</div>
);
I'm super new to react but excited about its potential. Still getting to grips with the fundamentals of it all so any explanation would be greatly appreciated.
I'm looking to render an 'About' component as the user clicks a button in the 'Nav' component (with the aim to toggle this later down the line).
I've attempted to do it in the simplest way I can think of, but this is obviously very wrong:
class Nav extends React.Component {
renderAbout() {
return (
<About />
);
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.renderAbout()}>About</h2>
</div>
</div>
</div>
);
}
}
Would this have something to do with updating the 'state' of the About component?
Thanks in advance.
You can use state to define if imported component About has to be rendered or not.
class Nav extends React.Component {
state = {
isAboutVisible: false,
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({ isAboutVisible: true }) }>About</h2>
</div>
</div>
{ this.state.isAboutVisible ? <About /> : null }
</div>
);
}
}
You currently do not have "About" component in actual view, you just render it somewhere out there, in the void!
To properly render a component you have to specify its place in JSX expression. Also, as one of the easiest solutions) you probably want to toggle it. So that translates to something like this:
constructor(props){
super(props)
this.state={toggle:false}
}
renderAbout(toggle) {
if(toggle)
return <About />
else return null;
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({toggle: !toggle})}>About</h2>
</div>
</div>
{this.renderAbout(this.state.toggle)}
</div>
);
}
}
Yes, you have to change state of the component. Changing the state will automatically rerender your component. In your example it should be something like:
class Nav extends React.Component {
state = {
showAbout: false; // initial state
}
renderAbout = () => {
if (!this.state.showAbout) return '';
return (
<About />
);
}
// ES6 priavte method syntax
handleButtonClick = () => {
this.setState({showAbout: true});
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={this.handleBtnClick}>About</h2>
{this.renderAbout()}
</div>
</div>
</div>
);
}
}
You could also consider using for example this package: https://www.npmjs.com/package/react-conditions
Also, remember that there is a rule that each method which listen for an event should start with the "handle" word. Like in may example.