How can I pass this state to my parent component?
I've tried using callbacks but since the output is in a Text field it doesn't seem to work. onChangeText doesn't recognize when text is changed in a text field? I've also tried using the callback on the Textinput but that doesn't seem to work either. Maybe I'm setting state in the wrong place.
Is this possible?
Child.js
getDLScore(e) {
let i;
i = deadliftScore.scoreSheet[e];
if (e != '') {
if (this.props.mosLevel === '1') {
if (e <= 180) {
return 'fail';
} else {
if (this.state.dlPoints != i) {
this.setState({ dlPoints: i });
}
return i;
}
}
}
}
render() {
return (
<View>
<View style={styles.eventContainer}>
<View styles={styles.child2}>
<Deadlift2
textChange={dlScoreInput => this.setState({ dlScoreInput })}
/>
</View>
<View styles={styles.child3}>
<Text style={styles.titleName}>Points</Text>
<Text style={styles.output}>
{this.getDLScore(this.state.dlScoreInput)}
// I want to pass this to my parent component
</Text>
</View>
</View>
</View>
);
}
const Deadlift2 = props => {
return (
<View>
<TextInput
style={styles.input}
onChangeText={dlScoreInput => props.textChange(dlScoreInput)}
value={props.dlScoreInput}
onKeyPress={props.getDLScore}
/>
</View>
);
};
Parent.js
render() {
return (
<View style={styles.screen2}>
<Text>Points - {this.state.dlScoreInput}</Text>
<View>
First issue is that you forgot to pass down the prop dlScoreInput into <Deadlift2/>. The value is props.dlScoreInput, so add that
<Deadlift2
textChange={dlScoreInput => this.setState({ dlScoreInput })}
dlScoreInput={this.state.dlScoreInput}
/>
The other is that you're not propogating the change up to the parent. You can apply the exact same logic you have that passes the information back up from <Deadlift2 /> to <Child />.
But at that point, you're passing data 2 levels up. After a certain point, you're better off using some type of global state management system (like redux). But if you're adamant about using local state every step of the way, I can show you how to write it.
Related
state = {
isGetOnceButtonClicked: false
}
doAddItems(){
this.state.isGetOnceButtonClicked= true;
}
buttonToggle(){
if(!this.state.isGetOnceButtonClicked){
return(
<Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/>
)
}else{
return(
<InputNumberSpinner></InputNumberSpinner>
)
}
}
render() {
return(
<ScrollView>
<View >
{ this.buttonToggle() }
</View>
</ScrollView>
}
I'm new to react-native. I want to show the component dynamically. So created the method in which return the button if condition satisfies else return created custom component but unfortunately it is showing error like "Text strings must be rendered within a component" If we return strings like "something" then it works fine but it throwing error whenever we are returning other than Text.
<ScrollView>
<View >
{ this.state.isGetOnceButtonClicked ? <InputNumberSpinner /> : <Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/> }
</View>
</ScrollView>
use a ternary operator for these kinds of tasks, if the value is true then it will show the spinner and if not, it will show the button
make sure to have a function that toggles the state of the button so buttonToggle will be like this:
buttonToggle(){
this.setState({isGetOnceButtonClicked: !this.state.isGetOnceButtonClicked});
}
I need to use different wrappers to render the screen based on a certain condition.
Example, I have a situation where I need to wrap the content in a view
<View>
<Text> </Text>
<View> </View>
{ and other elements here }
</View>
But in a different scenario, I need the View to be Content from nativeBase.
if useContent variable is true, then render everything using Content as a wrapper, else use View.
How would I best do that ?
Use a ternary operator to help you conditionally render.
render() {
const useContent = true;
return useContent ? (
<Content>
<Text></Text>
<View></View>
</Content>
) : (
<View>
<Text></Text>
<View></View>
</View>
)
}
It may also be better to extract these two pieces into their own components. Just so your component file doesn't become too large and maintainability starts to be a concern. Edit: If you want to keep the children of the component the same and just the wrapping element to change create a second component that renders children:
class WrappingElement extends Component {
render() {
const { children, useContent } = this.props;
return useContent ? (
<Content>{children}</Content>
) : (
<View>{children}</View>
)
}
}
class Foo extends Component {
render() {
<WrappingElement useContent={false}>
<Text>Hello World!</Text>
<View></View>
</WrappingElement>
}
}
<View>
booleanCondition1() && <Text> </Text>
booleanCondition2() && <View> </View>
booleanConditionN() && { and other elements here }
</View>
<View>
{condition == true ?
(
<Text> </Text>
<View> </View>
)
:
(
{ and other elements here }
)
}
</View>
I'm wondering if there is any possibility to find out from this.props.children if a specific child has been mounted?
The reason why I am asking this is that I would like to create a wrapper component that would deal with some sort of async rendering (specifically for react-native) and render children one after another. Just wondering how that could be accomplished using a simple wrapper component.
Any help would be appreciated.
Edit:
Just to describe the expected result more. Let's say I have a render method that has something like
<View>
<Text>I'm a string</Text>
<View>
<Image />
<Image />
<Image />
<Image />
</View>
<View>
<View />
<View />
</View>
</View>
I would like to create a wrapper that would asynchronously render each of the blocks and have something like
<ASyncRenderWrapper>
<Text>I'm a string</Text>
<View>
<Image />
<Image />
<Image />
<Image />
</View>
<View>
<View />
<View />
</View>
</ASyncRenderWrapper>
Now instead of assigning a ref to each child I would like to go over this.props.children and render them one by one, but to render the next child I would have to know that the previous has mounted.
Some pseudocode to illustrate what I think the wrapper component should look like or do
state = {
childRenderList: Array.apply(null, Array(props.children.length)).map(() => false),
}
refCounter = 0
componentDidMount() {
this.startAsyncRender()
}
startAsyncRender = () => {
if (children[refCounter]) {
// Somehow figure out if the previous child has been mounted to know if this one can be allowed to mount
// Alternativly I guess I could just skip a render cycle with setTimeout
childRenderList = childRenderList[refCounter] = true;
this.setState({ childRenderList: childRenderList });
if (children[refCounter + 1]) {
this.startAsyncRender();
this.refCounter++
}
}
}
canRender = (index) => {
return childRenderList[index];
}
return (
{ children.map((Child, index) => canRender(index) && <Child />) }
)
In your child component:
state = {
domMounted: false //initial state
}
componentDidMount() {
this.setState({domMounted: true})
}
<Child ref={(childRef) => {this.childRef = childRef} />
In your parent component:
this.childRef.state.domMounted
I am using react native, redux and flatlist. I render 10 items and then fetch / render the next 10 items on click (load more). My problem is that when I use setState(to increase the page counter) (thats why I dont use setstate in my code) to fetch more items (i use pagination) or ask whether or not Im fetching (true when fetching -> shown loading sign, false when fetched -> showing items), that triggers
a re-render and my view gets scrolled to top.
Ive been reading for a while now and event.preventDefault (as sometimes suggested) doesnt work and I do believe that my problem is the key / parent tree of the corresponding element changes between renders. I do give each item the key of its ID which is always unique. Am I doing it wrong? Or what else could be causing this? Thanks!
class App extends React.Component {
state = {
page : 0
}
componentDidMount() {
this.props.fetchData(0)
this.state.page++;
}
load = () => {
this.props.fetchData(this.state.page);
this.state.page++;
}
renderRow = ({item}) => {
return (
<Item
key={item.id}
coin={item}
/>
)
}
renderButton() {
return <Button
title="Load more"
onPress={this.load}
/>
}
render() {
if (this.props.isFetching) {
return (
<View>
<Text>Loading</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => {
return {
isFetching: state.data.isFetching,
data: state.data.data
}
}
export default connect(mapStateToProps, {fetchData})(App)
Hey change your render method return like below :
return (
{ this.state.isFetching && <View> <Text>Loading</Text> </View>}
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
{
element.answers_data.map((answer, index) =>
<View key={index} style={{paddingLeft: 20}}>
<TextInput
value = {answer.answer_text}
onChangeText={(answer_text) => {
this.setState({answer_text: answer_text});
}}
/>
</View>
)}
I am using TextInput in array. But it is not working. Please provide any solution. Thanks in advance
If your text inputs is an array, you should make its reflecting state an array too:
constructor(props) {
super(props);
this.state = {
answers: element.answers_data.map( (answer, index) => {
return answer.answer_text
}),
}
}
render() {
return (
<View>
{
element.answers_data.map((answer, index) =>
<View key={index} style={{paddingLeft: 20}}>
<TextInput
value = {this.state.answers[index]}
onChangeText={(answer_text) => {
/// Since state is immutable, construct a new array for modified answer for specific index.
this.setState({
answers: [
...this.state.answers.slice(0, index),
answer_text,
...this.state.answers.slice(index+1, this.state.answers.length)
]
});
}}
/>
</View>
)
}
</View>
)
}
Since state is immutable, you cannot just change the array value with index.
Check code for example to construct a new array for modified answer.
Do you try with multiple inputs change the same one state field?
May you should separate?
About is not working, what do you mean, the component updates but state doesn't change, callback doesn't invoke, you get always the same answer_text, or something else?