In below code there is "opened" attribute and I want to change its value by using ref. Here I am using ref as indexed array.
<Menu renderer={renderers.SlideInMenu} ref={(Menu) => { this.rowRefs[item.id] = Menu; }} opened={false}>
I tried it as
function updateRef(id){
React.findDOMNode(this.refs.id).setAttribute("opened", true);
}
Can anyone please explain how to create an indexed reference and how to use it?
Props should be immutable and for the purpose of dynamically change update them you should consider to set them via state.
Your code should look like:
<Menu renderer={renderers.SlideInMenu} ref={component => this.menuRef = component }} opened={this.state.opened}>
In which case the <Menu .. > is assumed to be rendered in a component which has a state variable opened which you can change using this.setState({opened: true}) . This state change will make your UI rerender hence <Menu .. > will be rendered with opened={true}.
Also if you want to use ref, then you should consider making a state variable inside Menu which should be initialized with opened prop, and you should have a method in the Menu which will change the state.
Your code should look like below:
class Menu extends React.Component {
constructor (props) {
super(props);
this.state = {
menuOpened: props.opened
}
}
changeMenuOpened = (value) => {
this.setState({
menuOpened: value
})
}
.....
}
and then you can just call the changeMenuOpened method using Menu's ref from the parent.
this.menuRef.changeMenuOpened(true);
Related
I probably misunderstood something very basic here.
I want to run a method in my child component whenever the state "oranges" in my parent component changes.
My Child component looks like this:
const [someText, setSomeText] = useState(props.oranges);
const consoleThis= () => {
if (someText == true){
console.log("win!")
}else{
return
}
};
useEffect(() => {
consoleThis();
}, [someText]);
This is the code in my parent component
...
const [apples, setApples] = useState(false);
<child
oranges ={apples}
/>
...
<Pressable
onPress={() => {
setApples(true);
}
>
Any ideas why I don't get my "win!" console logged?
useState(initial) doesn't reset the state each time the value passed to it changes - it is only used for setting the initial state of the value. Passing a new prop value will not update the state variable value - the state value becomes its own copy of the initial value that is managed independently of the prop value. In general, when you're lifting the state up, you don't want to also be keeping state in your child component. Instead, just drive the child component off of the state value passed via props.
You probably want something that responds to changes in the orange prop, like this:
function ChildComponent({orange}) {
useEffect(() => {
if (orange) {
console.log("win!")
}
}, [orange])
};
I am rendering a component for every item in a flatList. Each component has a label, and when the component is rendered, I have a useEffect that fetches the updated label name for that specific label.
For some reason, it seems to only be running for the last item in the flatList. The last item is the only item with the updated name, while all other still contain the outdated information.
Assuming there is an updated name for each label, why could my useEffect only be running on the last item?
<FlatList
data={labels}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
Label.js - I would think this would run for every label component rendered. Could there be a possible issue with what I have here? Or must it be somewhere else in my code?
let name = label.name;
useEffect(() => {
updateLabel()
name = label.name
}, [label]);
return (
<>
{name}
</>
)
I see several possible issues. Some important code is missing, so I'll answer what I can.
You're not using state to hold your label name in the Label component (name = label.name), so React will never know to re-render the component when it changes. It's rare to need to use a let variable in React. To hold properties that the component needs to change, use the useState hook.
However, you shouldn't do that here, because of the next point.
It looks like you are updating the label somewhere else, and also locally (name = label.name). Don't do this, it's too easy for the two to get out of sync and cause bugs. If the name is coming from somewhere else, show it and set it from props.
I'm not sure what updateLabel() does or where it comes from (how does the function know what to update the label to?), but if you need it, it should come from props.
If label.name is a string, you can't render it in a fragment. You must render it in a Text component. <Text>{label.name}</Text>
The object that FlatList passes in to the renderItem callback does not have a property called label, you are looking for item - this is the object from the data prop.
function renderLabel({ item }) { // item, not label
return <Label label={item} onPress={() => onPressLead(item)}/>;
}
const Label = ({ label, updateLabel }) => {
// no local label variable
useEffect(() => {
updateLabel(); // what is this supposed to do?
}, []); // no dependencies, if you only want to update the label once on mount
return <Text>{label.name}</Text>; // if label.name is a string
};
// your FlatList is fine as written
Your use effect probably needs the label as a dependency.
useEffect(() => {
updateLabelName()
}, [label]);
I have a component that dynamically renders different components based off of props. I am unable to set the ref on one of them. This question had a duplicate ref, which I do not have. In addition, the components are built inside the render function.
Here is my code:
render() {
let content = null
switch(this.props.type) {
case 'follow'
content = <View ref={comp => this._followBtn = comp} />
break
// ...etc
}
return (
<View ref={comp => (this._wrapper = comp)>
{ content }
</View>
)
}
I need access to _followBtn from a parent component rendering the _wrapper.
I have only accomplished accessing _wrapper.
Does my problem stem from the fact that these are dynamically generated?
The ref prop never seems to be called.
I'm using react-native-chooser to create my dropdowns and when I select 1 item from dropddown1 I want to update the items from dropdown2. Thanks.
In react, to make your UI change, you need to update your state. From looking at the docs for react-native-chooser it has a callback method called onSelect. Here, the currently selected option is returned to you to use. Based on this selected option you can update the state of second dropdown. The important part here is the parent child relationship. In react, a child is only re-rendered if the parent's state is updated (unless otherwise specified). Some pseudocode:
// Your method callback
onSelect = (option) => {
const newOptions = computeNewOptions(option)
this.setState({options: newOptions})
}
// Your Second dropdown component would take these options in as a prop
render () {
return (
<SecondDropDown options={this.state.options} />
)
}
// You can then access your options through the props
export default class SecondDropDown extends React.Component {
render () {
let myOptions = renderOptions(this.props.options)
return (
<View>
{myOptions}
</View>
)
}
}
React Native ListView: Rows are not re-rendering after datasource state has changed.
Here is a simplified version of my code:
render(): {
return <ListView
dataSource={this.state.DS}
renderRow={this.renderRow}/>
}
renderRow(item): {
return <TouchableOpacity onPress={() => this.handlePress(item)}>
{this.renderButton(item.prop1)}
</TouchableOpacity>
}
renderButton(prop1): {
if (prop1 == true) {
return <Text> Active </Text>
} else {
return <Text> Inactive </Text>
}
}
handlePress(item): {
**Change the prop1 of *item* in an array (clone of dataSource), then**
this.setState({
DS: this.state.DS.cloneWithRows(arrayFromAbove)
})
}
According to Facebook's example, ListView is supposed to rerender every time data source is changed. Is it because I'm only changing a property of an item in data source? It seems like renderRow function is not re-rendering, but render() function is from datasource change.
Thank you.
react is smart enough to detect changes in dataSource and if the list should be re-rendered. If you want to update listView, create new objects instead of updating the properties of existing objects.
The code would look something like this:
let newArray = this._rows.slice();
newArray[rowID] = {
...this._rows[rowID],
newPropState: true,
};
this._rows = newArray;
let newDataSource = this.ds.cloneWithRows(newArray);
this.setState({
dataSource: newDataSource
});
You can read more about similar issue on Github
First you need to set the datasource in the getInitialState function. Then, change the datasource by calling this.setState({}) and passing in the new datasource. It looks like you may have been on the right track above, but I have set up a working example of changing the ListView datasource here . I hope this helps
https://rnplay.org/apps/r3bzOQ