Reputation: 1042
I know this kind of manipulations might be unrecommended but in my situation I cannot find any other solution. I have a reference to component instance and I want to reach its parent, update style and re-render. If you think this is crazy I'll describe the problem.
I am using section list with sticky header and it generates tree like this (simplified):
<VirtualizedList>
<CellRenderer> //internal component rendered by Virtualized list with flex: 1
<MyComponent ref=(instance) => changeStylesFunc(instance) />
</CellRenderer>
</VirtualizedList>
I am passing MyComponent
but event if i set its style to width: 80
wrapper (CellRenderer) will have full width because of its own styles.
T cannot change CellRenderer
style because that would involve RN source code changes.
My idea:
since I have ref to MyComponent
instance I can also reach parent instance (CellRenderer
) by:
const cellRenderer = ref._reactInternalInstance._currentElement._owner
and I am stuck here because I had no luck with updating styles for cellRenderer
.
What I've tried:
cellRenderer._instance.props.styles = { width: 80 };
cellRenderer._currentElement.props.styles = { width: 80 };
Upvotes: 1
Views: 727
Reputation: 22797
This is another way to change visual width from <SectionList />
items, without touching <VisualizedList />
at all.
export class Home extends Component {
constructor(props) {
super(props);
this.state = {};
}
getUidFromSectionAndRow(section, row) {
return `${section}_${row}`;
}
getStyleWithUid(uid) {
return this.state[uid] || {flex: 1};
}
setStyleWithUid(uid, style) {
this.setState({
[uid]: style
});
}
render() {
let sectionData = [
[1,2,3],
[1,2,3]
]
return (
<SectionList
renderItem={({item}) => {
return (
<View style={[this.getStyleWithUid(item.key), {flexDirection: 'row', marginVertical: 10}]}>
<Text style={{flex: 1, textAlign: 'center', textAlignVertical: 'center', backgroundColor: 'white'}}>This is number {item.value} row.</Text>
<TouchableOpacity onPress={() => {
this.setStyleWithUid(item.key, {width: 200});
}}>
<Text style={{width: 80, height: 50, backgroundColor: 'purple', textAlign: 'center', textAlignVertical: 'center', color: 'white'}}>Change Width</Text>
</TouchableOpacity>
</View>
)
}}
renderSectionHeader={ ({section}) => <Text style={{fontSize: 20, fontWeight: '600'}}>{section.title}</Text> }
sections={
[
{ data: [
{
key: this.getUidFromSectionAndRow(1, 1),
value: 1
},
{
key: this.getUidFromSectionAndRow(1, 2),
value: 2
},
{
key: this.getUidFromSectionAndRow(1, 3),
value: 3
}
], title: 'Section 1' },
{ data: [
{
key: this.getUidFromSectionAndRow(2, 1),
value: 1
},
{
key: this.getUidFromSectionAndRow(2, 2),
value: 2
},
{
key: this.getUidFromSectionAndRow(2, 3),
value: 3
}
], title: 'Section 2' },
]
}
/>
);
}
}
This 70 lines of code should be simple enough, the key is to make every section / row reflect to an unique key in state.
So every time you set style with setStyleWithUid()
, it only effects to that specific row.
Upvotes: 0
Reputation: 22797
I think what you need is Lifting State Up:
Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor. Let’s see how this works in action.
In your case, <CellRenderer />
and <MyComponent />
need to reflect the same changing data.
class MyCell extends Component {
constructor(props) {
super(props);
this.state = {
cellStyle: { flex: 1 }
}
}
changeStyleFunc(newStyle) {
this.setState({ cellStyle: newStyle });
}
render() {
return (
<CellRenderer style={this.state.cellStyle}>
<MyComponent onChangeStyle={(newStyle) => changeStylesFunc(newStyle)} />
</CellRenderer>
);
}
}
So you can use:
<VirtualizedList>
...
<MyCell />
...
</VirtualizedList>
Note:
Don't mess up by modifying props from ref
like that. It won't work, since An update can be caused by changes to props or state. Not by changing their inner, immutable value.
Upvotes: 2