I'm working on a React Native project and I realized that React Native seems to break the React flow (Parent to children) props update.
Basically, I'm calling a "Menu" component from an "App" component, passing a prop to "Menu". However, when I update the "App" state, the props on "Menu" should update, but this doesn't happen. Am I doing something wrong?
That's my code:
App.js
import React from 'react';
import {
View,
Text
} from 'react-native';
import Menu from './Menu';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
opacity: 2
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
opacity: 4
});
}, 3000);
}
render() {
return(
<View>
<Menu propOpacity={this.state.opacity} />
</View>
);
}
}
export default App;
Menu.js
import React from 'react';
import {
View,
Text
} from 'react-native';
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
menuOpacity: props.propOpacity
}
}
render() {
return(
<View>
<Text>Menu opacity: {this.state.menuOpacity}</Text>
</View>
);
}
}
Menu.propTypes = {
propOpacity: React.PropTypes.number
}
Menu.defaultProps = {
propOpacity: 1
}
export default Menu;
React is not breaking data flow... You are. After initial state initialisation, you forget to update Menu's state later, when parent sends updated props.
Try this...
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
menuOpacity: props.propOpacity
}
}
componentWillUpdate(nextProps, nextState) {
nextState.menuOpacity = nextProps.propOpacity;
}
render() {
return(
<View>
<Text>Menu opacity: {this.state.menuOpacity}</Text>
</View>
);
}
}
Related
I am trying to get props via state object.
import React, { Component } from 'react';
import Counter from './counter';
class Counters extends Component {
state = {
counters: [
{id:1, value:1},
{id:2, value:2},
]
};
render() {
return(
<div>
this.state.counters.map(counter => (
<Counter key={counter.id} value={counter.value}/>))}
</div>)
}
}
export default Counters;
import React, { Component } from 'react';
class Counter extends Component {
state = {
value: this.props.value
};
constructor(){
super();
}
render() {
console.log(this.props);
return (
<div>
</div>
);
}
}
export default Counter;
Why can't I get props within state object? I am seeing props in render.
I am getting TypeError: Cannot read property 'value' of undefined.
Can someone show me how to pass props correctly to the state object. I have seen props passed this way by other coders.
Thanks,
Rob.
You need to pass props to super and define the state in the constructor or remove the constructor and use state directly.
CODESANDBOX
import React, { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value
};
}
render() {
console.log(this.props);
return <div>{this.state.value}</div>;
}
}
export default Counter;
or CODESANDBOX
import React, { Component } from "react";
class Counter extends Component {
state = {
value: this.props.value
};
render() {
console.log(this.props);
return <div>{this.state.value}</div>;
}
}
export default Counter;
I am trying to get state of a class from another class but it throws an error "cannot read property 'state' od undefined". My approach is when the user press the button the "FromStr" state get redirect to another class B from A. i tried
import React, { Component } from "react";
import { StyleSheet, Text, View, TextInput, Button } from "react-native";
import styles from "./appstyles";
import {getValue} from "./main"
export default class A extends Component{
constructor (props) {
super(props)
this.state={
From:'',
FromStr:'',
}
}
changeText=(From)=>{
this.setState({From})
}
onPress = ()=>{
this.setState({FromStr: this.state.From})
this.fetch()
}
fetch(){
getValue();
}
render(){
return (
<View>
<View style={styles.inputFields}>
<TextInput placeholder="From" id="from" style={styles.fromField} onChangeText={this.changeText} />
<View style={styles.buttonStyle}>
<Button
title={"Go Back"}
color="#f194ff"
onPress={this.onPress}
></Button>
);
}
}
Class B
import React, { Component } from "react";
export function getValue(){
alert(this.state.FromStr);
}
Shared state between components by direct access is an anti-pattern. Each component should have its own state. If you need globally, please consider using Redux.
Passing state as props is also valid, but it only works when components are in parent-child order. Redux allows components to be updated irrelevant of their relationship
As mentioned , pass state as props to their children.
class classname2 extends React.Component {
this.state = { statename1: "lala" };
render() {
return <classname1 statename1={this.state.statename1} />
}
};
class classname1 extends React.Component {
render() {
return (
<div>{this.props.statename1}</div>
);
}
};
You can define class A's this as global inside it's container. Then, call it from class B. For example;
//class A constructor
constructor(props){
super(props);
global.__classAThis = this;
}
//class B constructor
constructor(props){
super(props);
console.log(__classAThis);
//also you can update class A this
__classAthis.setState({test: true})
}
There are many ways to do this. The simplest way is to use LocalStorage .
Usage
A screen
this.setState({FromStr: this.state.From
}, () => localStorage.setItem('FromStr', this.state.From));
B screen
componentDidmount(){
const data = localStorage.getItem('FromStr')
alert(data);
}
I'm really regarding props in React/React-Native. I have a parent view. In this view I'm getting the user data from a LocalStorage.['
import React, { Component } from 'react';
import { Container, Content, View } from 'native-base';
import NutrionalToolbar from '../../components/NutrionalToolbar';
import { AsyncStorage } from 'react-native';
export default class LogsScreen extends Component {
state = {
user: '',
}
componentWillMount() {
this._bootstrapAsync();
}
_bootstrapAsync = async () => {
const user = await AsyncStorage.getItem('user');
this.setState({ user: JSON.parse(user) })
};
render() {
return (
<Container>
<NutrionalToolbar user={this.state.user} />
</Container>
);
}
}
Now inside the NutrionalToolbar component I have this.
import React, { Component } from 'react';
import { View } from 'native-base';
class NutrionalToolbar extends Component {
constructor(props) {
super(props)
console.log(this.props) // This renders an empty user object
}
render() {
console.log(this.props) // This renders the user object with values
return (
<View>
</View>
);
}
}
export default NutrionalToolbar;
How can I get this.props values inside the constructor. I'm getting the values inside render method. Why isn't working inside the constructor?
I would recommend looking into the componentDidUpdate lifecycle hook because, even if you could access the initial user prop in the constructor, you wouldn't be able to access updates to that prop in the constructor.
import React, { Component } from 'react';
import { View } from 'native-base';
class NutrionalToolbar extends Component {
componentDidUpdate() {
console.log(this.props) // This will always log the current props
}
render() {
return (<View></View>);
}
}
export default NutrionalToolbar;
I've got a Meteor app using React. I've added Session variables and want to pass the new Session value (which will be another React component) into another react component.
The user will click the p-tag in the SideNav and reset the Session to a React component.
SideNav component:
import React from 'react';
import { Session } from 'meteor/session';
import SonataContent from './sonata-content';
export default () => {
injectSonataText = () => {
const sonataContent = <SonataContent/>;
Session.set('MainContent', sonataContent); /* Set Session value to component */
};
return (
<div className="side-nav">
<h2>Explore</h2>
<p onClick={this.injectSonataText.bind(this)}><i className="material-icons">child_care</i><span> Sonatas</span></p>
</div>
)
}
In the MainWindow, Tracker.autorun re-runs and sets the state to the component and renders the new state value.
Main Window component:
import React from 'react';
import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
export default class MainWindow extends React.Component {
constructor(props) {
super(props);
this.state = {
text: ""
}
}
componentDidMount() {
this.mainWindowTracker = Tracker.autorun(() => {
const text = Session.get('MainContent');
this.setState({text: text});
});
}
componentWillUnmount() {
this.mainWindowTracker.stop();
}
render() {
return (
<p>{this.state.text}</p>
)
}
}
I'm getting an error "Invariant Violation: Objects are not valid as a React child". Is this caused by the component being used in setState? Is there a way to do this?
Session set function accepts as a value EJSON-able Object which I think may not work with React Object.
However I would try (only a guess though):
injectSonataText = () => {
Session.set('MainContent', SonataContent); /* Set Session value to component */
};
...
export default class MainWindow extends React.Component {
constructor(props) {
super(props);
this.state = {
Component: null,
}
}
componentDidMount() {
this.mainWindowTracker = Tracker.autorun(() => {
const MainContent = Session.get('MainContent');
this.setState({Component: MainContent});
});
}
componentWillUnmount() {
this.mainWindowTracker.stop();
}
render() {
const { Component } = this.state;
return (
<p>
{
Component && <Component />
}
</p>
)
}
}
I'm writing a library full of ReactJS components, so Flux should not be used, since it's a library.
I have a component, a ThemeProvider.
import React from 'react';
class OfficeUIThemeProvider extends React.Component {
constructor(props) {
super(props);
}
render() {
return null;
}
}
OfficeUIThemeProvider.propTypes = {
theme: React.PropTypes.oneOf(['Office2016']).isRequired,
color: React.PropTypes.oneOf(['Light-Blue', 'Blue', 'Green', 'Orange', 'Purple', 'Red']).isRequired
};
export default OfficeUIThemeProvider;
I return null in the render() method since this component should not render anything.
Then I do have a simple component, a button.
import React from 'react';
class OfficeUIButton extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className={"officeui-button"}>
<span className="{officeui-button-label}">{this.props.label}</span>
</div>
}
}
OfficeUIButton.propTypes = {
label: React.PropTypes.string.isRequired,
};
export default OfficeUIButton;
Now, I want the button to have specific classes, based on the values provided in the ThemeProvider.
A simple solution would be to render the OfficeUIButton component directly in my ThemeProvider render() method but this is not a valid solution since I'm developing a library and don't want to couple things.
An application using this library should work as:
ReactDOM.render(
<OfficeUIThemeProvider theme='Office2016' color='Light-Blue'>
<OfficeUIButton label="To..." />
</OfficeUIThemeProvider>,
document.getElementById('app')
);
But, this renders nothing since my ThemeProvider return nullâ—‹ in it'srender` method.
How can this be accomplished?
Kind regards,
OfficeUIButton is child component of OfficeUIThemeProvider, so I suppose you should try:
class OfficeUIThemeProvider extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{this.props.children}
</div>
);
}
}