I currently creating a dynamic design UI. I'm using an axios call too get the dynamic design properties like background color, font color, images to be displayed.
import React, {Component} from "react";
import MainButton from '../utilities/MainButton';
const tinycolor = require("tinycolor2");
const styles = {
underlineStyle: {
borderColor: blue800
}
}
class MobileHome extends React.Component {
constructor(props) {
super(props);
this.color = '';
this.state = {
mainColor: '',
fontColor: ''
}
}
componentWillMount() {
axios.get('/getEventConfig').then(res => {
var config = res.data;
var color = tinycolor(config.COLOR);
var font = '#fff';
if (color.isLight()){
font = '#000';
}
this.color = color;
this.setState({mainColor: config.COLOR}); // error on timing issue
console.log('set state', this.state.mainColor);
});
}
render() {
return (
<div className="center-panel">
<TextField
hintText="Enter Code Here"
fullWidth={true}
underlineFocusStyle={styles.underlineStyle}
id="event_code" />
<MainButton
label="Upload Photo"
fullWidth={true}
size="xl"
color={color}
fontcolor={this.state.fontColor}
onClick={this.uploadPhoto}/>
</div>
)
}
}
export default MobileHome
On which, my MainButton is another component, just calling the Material UI RaisedButton:
class MainButton extends React.Component {
constructor(props) {
super(props);
console.log('p', this.props);
let mainColor = _.isNil(this.props.color) ? '#2E65C2': this.props.color;
let fontColor = _.isNil(this.props.fontColor) ? '#FFF': this.props.fontColor;
this.styles = {
root: {
width: 225
},
label: {
color: fontColor,
paddingTop: 5
},
color: mainColor,
}
}
render() {
return (
<RaisedButton
label={this.props.label}
fullWidth={this.props.fullWidth}
buttonStyle={this.styles.button}
style={this.styles.root}
backgroundColor={this.styles.color}
labelStyle={this.styles.label}
onClick={this.props.onClick}
/>
)
}
}
export default MainButton;
The problem is, the MainButton is rendered already before the axios call is completed. I'm looking for some way, for the render to wait before the axios call to be completed, before it shows the UI. Is that possible?
You can use ternary operator to show MainButton based on state change.
Check below code, here I have taken new state variable resReceived and setting it to false by default. In API res setting to true.
import React, {Component} from "react";
import MainButton from '../utilities/MainButton';
const tinycolor = require("tinycolor2");
const styles = {
underlineStyle: {
borderColor: blue800
}
}
class MobileHome extends React.Component {
constructor(props) {
super(props);
this.color = '';
this.state = {
mainColor: '',
fontColor: '',
resReceived: false
}
}
componentWillMount() {
axios.get('/getEventConfig').then(res => {
var config = res.data;
var color = tinycolor(config.COLOR);
var font = '#fff';
if (color.isLight()){
font = '#000';
}
this.color = color;
this.setState({mainColor: config.COLOR, resReceived: true}); // error on timing issue
console.log('set state', this.state.mainColor);
});
}
render() {
return (
<div className="center-panel">
<TextField
hintText="Enter Code Here"
fullWidth={true}
underlineFocusStyle={styles.underlineStyle}
id="event_code" />
{this.state.resReceived ?
<MainButton
label="Upload Photo"
fullWidth={true}
size="xl"
color={color}
fontcolor={this.state.fontColor}
onClick={this.uploadPhoto}/>
: ''}
}
</div>
)
}
}
export default MobileHome
OR
If you want to keep the button disabled until you receive a response then try below one.
import React, {Component} from "react";
import MainButton from '../utilities/MainButton';
const tinycolor = require("tinycolor2");
const styles = {
underlineStyle: {
borderColor: blue800
}
}
class MobileHome extends React.Component {
constructor(props) {
super(props);
this.color = '';
this.state = {
mainColor: '',
fontColor: '',
disableMainButton: true
}
}
componentWillMount() {
axios.get('/getEventConfig').then(res => {
var config = res.data;
var color = tinycolor(config.COLOR);
var font = '#fff';
if (color.isLight()){
font = '#000';
}
this.color = color;
this.setState({mainColor: config.COLOR, disableMainButton: false}); // error on timing issue
console.log('set state', this.state.mainColor);
});
}
render() {
return (
<div className="center-panel">
<TextField
hintText="Enter Code Here"
fullWidth={true}
underlineFocusStyle={styles.underlineStyle}
id="event_code" />
<MainButton
label="Upload Photo"
fullWidth={true}
size="xl"
color={color}
fontcolor={this.state.fontColor}
onClick={this.uploadPhoto}
disabled = {this.state.disableMainButton}
/>
}
</div>
)
}
}
export default MobileHome
Here is the Problem:
mainColor and fontColor are init in constructor of MainButton.
constructor can be init only once, so those two dynamic fields can never be dynamic.
Here is the solution:
Move the styles to render function so that the colors can be dynamic when data change.
Tips: check the react doc https://reactjs.org/docs/state-and-lifecycle.html.
render() {
const { fontColor, mainColor } = this.props;
const styles = {
root: {
width: 225
},
label: {
color: _.isNil(this.props.fontColor) ? '#FFF': fontColor,
paddingTop: 5
},
color: _.isNil(this.props.color) ? '#2E65C2': mainColor,
}
return (
<RaisedButton
label={this.props.label}
fullWidth={this.props.fullWidth}
buttonStyle={styles.button}
style={styles.root}
backgroundColor={styles.color}
labelStyle={styles.label}
onClick={this.props.onClick}
/>
)
}
Related
I managed to pass initial route name from parent navigator to child navigator, but Log Out button in Drawer is not working (do nothing, no errors). Is it because I created multiple AppContainers?
NavApp.js
const routeConfigs = {
NavGuest: { screen: NavGuest },
NavDrawer: { screen: NavDrawer }
}
const AppContainerIn = (props) => {
navConfigs.initialRouteName = props.initialRouteName;
let switchNav = createSwitchNavigator(routeConfigs, navConfigs);
let AppContainerOut = createAppContainer(switchNav);
return <AppContainerOut />
}
export default class NavApp extends Component {
render() {
return (
<AppContainerIn initialRouteName={this.props.initialRouteName} />
)
}
}
NavDrawer.js
const routeConfigs = {
Wizard: { screen: Wizard },
NavHomeSearch: { screen: NavHomeSearch },
}
const navConfigs = {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width - 120,
}
const ContainerDrawerIn = (props) => {
navConfigs.initialRouteName = props.initialRouteName;
let NavDrawer = createDrawerNavigator(routeConfigs, navConfigs);
let ContainerDrawerOut = createAppContainer(NavDrawer);
return <ContainerDrawerOut />
}
export default class ContainerDrawer extends Component {
render() {
return (
<ContainerDrawerIn initialRouteName={this.initialRouteName} />
)
}
}
SideMenu.js
export default class SideMenu extends Component {
constructor(props) {
super(props);
this.navigation = props.navigation;
}
logout = () => {
AsyncStorage.removeItem('isLoggedin');
// Help, not going anywhere. Btw, isLoggedin is successfully removed.
this.props.navigation.navigate('NavGuest');
}
render() {
return (
<View>
<Button title='Log Out' onPress={() => this.logout()} />
</View>
)
}
}
Common mistake: Explicitly rendering more than one navigator.
https://reactnavigation.org/docs/en/common-mistakes.html
export default class App extends React.Component {
render() {
/* In the root component we are rendering the app navigator */
return <AppContainer />;
}
}
const AuthenticationNavigator = createStackNavigator({
SignIn: SignInScreen,
ForgotPassword: ForgotPasswordScreen,
});
class AuthenticationScreen extends React.Component {
static router = AuthenticationNavigator.router;
render() {
return (
<AuthenticationNavigator navigation={this.props.navigation} />
);
}
}
const AppNavigator = createSwitchNavigator({
Auth: AuthenticationScreen, // This screen renders a navigator!
Home: HomeScreen,
});
const AppContainer = createAppContainer(AppNavigator);
I want to edit other componenet's state by running function which checks if the current state of the app is active, Which means it runs each time the app come to the forground.
let d = new Date().getHours();
let title = messages[`_${d}`].message;
function getAppState() {
AppState.addEventListener("change", (state) => {
if(state == "active") {
d = new Date().getHours();
title = messages[`_${d}`].message;
// App.setState({ text: title }); **Not working!
console.log(title);
}
});
}
export default class App extends React.Component {
constructor() {
super();
this.state = { text: title };
getAppState();
}
render() {
return (
<View>
<StatusBar hidden={ true } />
<Text style={styles.title}>{title}</Text>
</View>
);
}
}
I want to change text's value according to the hour.
I don't have an environment to test this but I would do it like this:
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = this.updateHour.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
updateTitle(state) {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}
If you wanted updateTitle() to be another function and is not part of the Component I would do this:
const updateComponentTitle = state => {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = udpateComponentTitle.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}
I'm new to react and I've been playing around with it the past few days. I'm trying to append a value to the DOM, I found a way to do it but I'm looking for a way to do with by
var para = document.createElement("li");
var t = document.createTextNode(value);
para.appendChild(t);
document.getElementById("root").appendChild(para);
But, I'm looking for a different way. I tried a few different ways but I'm stuck. Is there a way other than this way above ^^^^
import React, { Component } from 'react';
import { render } from 'react-dom';
export class Parent extends React.Component {
constructor(props) {
this.greet = this.greet.bind(this);
this.state = { text: " " };
}
greet(value) {
//console.log(value);
var para = document.createElement("li");
var t = document.createTextNode(value);
para.appendChild(t);
document.getElementById("root").appendChild(para);
}
render() {
return (
<div>
<Child onGreet={this.greet} />
</div>
)
}
};
export class Child extends React.Component {
constructor(props) {
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.eventClick = this.eventClick.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
eventClick() {
this.props.onGreet(this.state.value);
}
render() {
return (
<div>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<button type="button" onClick={this.eventClick}>Submit </button>
</div>
)
}
};
You can try to utilize React's state property.
import React, { Component } from 'react';
import { render } from 'react-dom';
export class Parent extends React.Component {
constructor(props) {
this.greet = this.greet.bind(this);
this.state = {
text: [],
};
}
greet(value) {
//console.log(value);
const {text} = this.state
return this.setState({
text: text.concat(value),
});
}
render() {
return (
<div>
<Child onGreet={this.greet} />
<ul>
{this.state.text.map(x => (<li>{x}</li>))}
</ul>
</div>
)
}
};
With this, the list gets populated as the value of this.state.text changes.
For some reason when I try to bind a function to a prop in my React component, it is coming up with
TypeError: Can't add property onSearch, object is not extensible.
I am not too familiar with what this means or why it is appearing, I think it may be to do with the es6 bindings which I am still finding my way around on.
Here are the two most relevant components.
Searchbox
import React from 'react';
import SearchForm from 'SearchForm';
import searchDisplay from 'searchDisplay';
import googleRequests from 'googleRequests';
class SearchBox extends React.Component {
constructor() {
super();
this.state = {
searchResults: []
}
this.handleSearch = this.handleSearch.bind(this);
}
handleSearch(searchTerm) {
googleRequests.search(searchTerm).then((response) => {
console.log(response.items);
this.extrapolateResults(response.items);
}), ((error) =>{
console.log(error)
});
}
//pull relevant data out of api call
extrapolateResults(arr) {
function Book(objInArr) {
this.link = objInArr.selfLink;
this.bookTitle = objInArr.volumeInfo.title;
this.author = objInArr.volumeInfo.authors;
this.bookDescription = objInArr.volumeInfo.description;
this.thumbnail = function() {
if (objInArr.volumeInfo.hasOwnProperty('imageLinks')){
return objInArr.volumeInfo.imageLinks.smallThumbnail
}
else {
return "No Thumbnail Available";
}
};
this.thumbnailPic = this.thumbnail();
}
//push extrapolated data into array
var finalRes = [];
var initRes = arr;
initRes.forEach(function (objInArr) {
var obj = new Book(objInArr);
finalRes.push(obj);
})
this.setState({
searchResults: finalRes
})
console.log(finalRes, this.state.searchResults)
}
render() {
var res = this.state.searchResults;
function renderResults() {
if (res.length !== 0) {
return (<SearchDisplay content={res} />)
}
else {
return;
}
}
var style = {
border: '1px solid black',
height: '80%',
width: '83%'
}
return (
<div style={style}>
<SearchForm onSearch={this.handleSearch}> </SearchForm>
</div>)
}
};
export default SearchBox;
Searchform
import React from 'react';
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.props.onSearch = props;
}
onFormSubmit(e) {
e.preventDefault();
var searchTerm = this.refs.searchTerm.value;
if (searchTerm.length > 0) {
this.refs.searchTerm.value = '';
this.props.onSearch(searchTerm);
}
}
render() {
var style = {
border: '1px solid black',
float: 'left',
height: '100%',
width: '30%'
}
return(
<div style={style} className="container">
<form onSubmit={this.onFormSubmit}>
<input type="text" placeholder="search name here" ref="searchTerm"/>
<input type="submit" className="button" value="Get Book"/>
</form>
</div>
);
}
};
export default SearchForm;
I have a feeling I am missing something very simple but after googling this issue for a while I still can't figure out what it is...
remove this.props.onSearch = props;... not sure what you wanted to do there on that line
change handleSearch function definition to handleSearch = () => {} fat arrow function
it will work fine in searchbox file.
I want to animate the depth of the whole Card when the mouse is over it.
I try this (so-so I'm new in React) but I have no idea how to do it:
<Card
linkButton={true}
href="/servicios/"
onClick={Link.handleClick} zDepth={3}
onMouseEnter={this.setState({zDepth={1}})}>
</Card>
Thanks in advance.
5 years later and there is still no correct answer, you do not have to set the component state when it hovers, just use the pseudo-class :hover:
<Card
sx={{
':hover': {
boxShadow: 20, // theme.shadows[20]
},
}}
>
If you want to use styled():
const options = {
shouldForwardProp: (prop) => prop !== 'hoverShadow',
};
const StyledCard = styled(
Card,
options,
)(({ theme, hoverShadow = 1 }) => ({
':hover': {
boxShadow: theme.shadows[hoverShadow],
},
}));
<StyledCard hoverShadow={10}>
<Content />
</StyledCard>
Live Demo
constructor(props) {
super(props);
this.state = { shadow: 1 }
}
onMouseOver = () => this.setState({ shadow: 3 });
onMouseOut = () => this.setState({ shadow: 1 });
<Card
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
>
Updated #1
Full example
// StyledCard.js
import React, { Component } from 'react';
import { Card } from 'material-ui/Card';
class StyledCard extends Component {
state: {
shadow: 1
}
onMouseOver = () => this.setState({ shadow: 3 });
onMouseOut = () => this.setState({ shadow: 1 });
render() {
return (
<Card
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
>
{this.props.children}
</Card>
);
}
export default StyledCard;
.
// Container.js
import React from 'react';
import StyledCard from './StyledCard';
const Container = () => [
<StyledCard>Card 1</StyledCard>,
<StyledCard>Card 2</StyledCard>,
<StyledCard>Card 3</StyledCard>,
];
export default Container;
UPDATED #2
With HOC
// withShadow.js
import React from 'react';
const withShadow = (Component, { init = 1, hovered = 3 }) => {
return class extends React.Component {
state: {
shadow: init
};
onMouseOver = () => this.setState({ shadow: hovered });
onMouseOut = () => this.setState({ shadow: init });
render() {
return (
<Component
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
{...this.props}
/>
);
}
};
};
export default withShadow;
.
// Container.js
import React from 'react';
import { Card } from 'material-ui/Card';
import withShadow from './withShadow';
const CardWithShadow = withShadow(Card, { init: 2, hovered: 4 });
const Container = () => [
<CardWithShadow>Card 1</CardWithShadow>,
<CardWithShadow>Card 2</CardWithShadow>,
<CardWithShadow>Card 3</CardWithShadow>,
];
export default Container;
#Alex Sandiiarov answer didnt work for me. The docs show to use the raised property.
https://material-ui.com/api/card/
class Component extends React.Component{
state = {
raised:false
}
toggleRaised = () => this.setState({raised:!this.state.raised});
render(){
return <Card onMouseOver={this.toggleRaised}
onMouseOut={this.toggleRaised}
raised={this.state.raised}>
...
</Card>
}
}