I have the data from contentful, the data is formatted like this and is converted using #contentful/rich-text-react-renderer with this conversion options. By calling the codes below to perform the conversion
import { documentToReactComponents } from '#contentful/rich-text-react-renderer';
const convert = (document: any) => {
const nodes = documentToReactComponents(document?.fields?.html, contentfulToReactnative);
if (nodes && Array.isArray(nodes)) {
const newNodes = nodes.map((component, index) => {
if (index + 1 === nodes.length) {
return component;
}
return <View key={uuid()}>{component}</View>;
});
return newNodes;
}
return nodes;
};
with that, I got arrays of <ForwardRef /> like this
[<ForwardRef />, <ForwardRef(Text) allowFontScaling={false}></ForwardRef(Text)>]
the question is, how should I display ForwardRefs in react native. By doing {elements} (elements is the variable holding that array) won't work.
I don't know yet if these ForwardRefs are the things to display.
Related
I am working on a react native app and I have an array of components that I need to be updated from different sections of my app. I am using context to be able to provide different consumers with this array, as well as the ability to the update the array.
As you can see in the code below I am passing my array, as well as a function to update the list, as the value for my provider. I then am using "header-right" in the navigation to call the function that updates the array in the provider state. I have confirmed that the array is updating and will only re-render the consumers when a change state in another component causes a re-render.
What I need is the change state of the provider to re-render all consumer components when it is changed, not when another components state change has occurred.
const DbContext = React.createContext("DEFAULT");
export default class DbProvider extends Component {
constructor(props){
super(props);
this.state = {
choreList: [],
refresh:null,
}
}
componentDidMount(){
sqlQueries.getChores().then(row => this.setChoresList(row));
}
updateChoreList = (choresAdded, offset) => {
console.log("K");
if(offset == 1){ // if adding chores
var tempList = this.state.choreList;
tempList.push(choresAdded);
this.setState({
choreList: tempList,
})
}else if(offset == -1){ // if removing a chore
for(var i = 0; i < this.state.choreList.length; i++){
for(var j = 0; j < choresAdded.length; j++){
if(this.state.choreList[i].state.name == choresAdded[i].state.name){ // if chore being removed is i in state choreList
var tempList = this.state.choreList; // remove and update state choreList
tempList.splice(i, 1);
this.setState({
choreList: tempList,
});
break;
}
}
}
}
}
setChoresList(row){
var tempList = [];
for(var i = 0; i < row._array.length; i++){
tempList.push(<Chore choreName={row._array[i].name} dayId={row._array[i].id} nav={this.props} inDB={true} key={row._array[i].name}/>);
}
this.setState({
choreList: tempList,
})
}
render () {
return (
<DbContext.Provider value={{choreList: this.state.choreList, update: this.updateChoreList}}>
{this.props.children}
</DbContext.Provider>
)
}
}
export { DbContext, DbProvider }
I am then in another component using this list to display something
render(){
return (
<DbProvider>
<DbContext.Consumer>
{globalChores => globalChores.choreList == 0
? <NoChores nav={this.props.nav} dayState={this} />
: (
<View style={styles.ChoreDayContainer}>
<View style={styles.ChoreListContainer}>
{globalChores.choreList}
</View>
<AddChoresBtn nav={this.props.nav} dayState={this}/>
</View>)}
</DbContext.Consumer>
</DbProvider>
);
}
and finally I am updating the provided array like so
componentDidMount(){
this.props.navigation.setOptions({
headerRight: () => (
<DbProvider>
<DbContext.Consumer>{(db) =>(
<Button
onPress={ () => this.dataToDB(db)}
title="Save"
color="#fff"
/> )}
</DbContext.Consumer></DbProvider>
),
})
}
dataToDB(db){
db.update(this.state.choreShowingList, 1);
}
}
Now when these tags are in component file how does one map through various values.
Example
1. <Image source = require("")/>
2. const { sound } = await Audio.Sound.createAsync(require("../../assets/sounds/slack.mp3")
You can map like this:
const { sound } = await Audio.Sound.createAsync(require("../../assets/sounds/slack.mp3")
return sound.map((item, index) => (
<YourComponent key={index}>{item.value}</YourComponent>
))
Im looking for insight on how best to implement a loadMore function in the onEndReached callback provided by flatlist while using apollo hooks! I've got it sort of working except every time i load more results the list jumps to the top since the data field of flatlist relies on incoming data from useQuery that changes every time it asks for more...
I dont know if i should be implementing offset and limit based pagination, cursor based, or some other strategy.
If anyone has tips that would be huge! thanks!
I am using Shopify storefront graphql queries to get product list, and here is how I have implemented pagination using cursor-based pagination method on FlatList. Hope you find something usable.
First, declare two variables which will be used later to check whether the Flatlist scrolled and reached on the end.
// declare these two variables
let isFlatlistScrolled = false;
let onEndReachedCalledDuringMomentum = false;
Now, create a method called handleFlatlistScroll which will be used to changed the values of the variable isFlatlistScrolled when the flatlist is scrolled.
const handleFlatlistScroll = () => {
isFlatlistScrolled = true;
};
Also declare a method to change the value of onEndReachedCalledDuringMomentum.
const onMomentumScrollBegin = () => {
onEndReachedCalledDuringMomentum = false;
}
Now, create your flatlist like this :
return (
<Layout style={{ flex: 1 }}>
<Query
query={GET_PRODUCT_LIST_BY_COLLECTION_HANDLE}
variables={{
handle: props.route.params.handle,
cursor: null,
}}>
{({
loading,
error,
data,
fetchMore,
networkStatus,
refetch,
stopPolling,
}) => {
if (loading) {
return <ProductListPlaceholder />;
}
if (data && data.collectionByHandle?.products?.edges?.length > 0) {
stopPolling();
return (
<FlatList
data={data.collectionByHandle.products.edges}
keyExtractor={(item, index) => item.node.id}
renderItem={renderProductsItem}
initialNumToRender={20}
onScroll={handleFlatlistScroll}
onEndReached={() => {
if (
!onEndReachedCalledDuringMomentum &&
isFlatlistScrolled &&
!isLoadingMoreProducts &&
!loading &&
data.collectionByHandle?.products?.pageInfo?.hasNextPage
) {
onEndReachedCalledDuringMomentum = true;
setLoadingMoreProductsStatus(true);
// your loadmore function to fetch more products
}
}}
onEndReachedThreshold={Platform.OS === 'ios' ? 0 : 0.1}
onMomentumScrollBegin={onMomentumScrollBegin}
// ... your other flatlist props
/>
);
}
return <EmptyProductList />;
}}
</Query>
</Layout>
)
As you can see in above code, load more function only called when flatlist is properly scrolled at the end.
I have two different components one is CategoryList which is a flatlist and regionlist is a section list. I would like to display the CategoryList first and when item clicked the regionlist will show however I m not sure why it is not working. (I use context to store the state)
{!isToggle ? (
<CategoryList></CategoryList>
) : (
<RegionList style={styles.regionListStyle}></RegionList>
)}
I also create a button to see if it is a problem about the context but it is not.
const ToggleContext = createContext(true);
export const useToggle = () => {
return useContext(ToggleContext);
};
export function ToggleProvideData({children}) {
const [isToggle, setToggle] = useState(true)
return <ToggleContext.Provider value={{isToggle,setToggle}}>
{children}
</ToggleContext.Provider>;
}
I just wonder conditional render is it not working for flatlist?
UPDATE: I tried create a state to store the useContext isToggle but it only appears for like 1 sec
I guess isToggle as a boolean variable, hence you can use the below code for rendering conditionally
{ (isToggle === true) &&
<CategoryList></CategoryList>
}
{ (isToggle === false) &&
<RegionList style={styles.regionListStyle}></RegionList>
}
I'm using React Native with NativeBase and would like to make the labels of my Picker more complicated than just one plain string of text.
But is it even possible to pass elements as the label, say multiple child elements wrapped in a single top-level element?
Or do Pickers only support plain text as labels?
As requested by bennygenel, here's a version of what I've tried:
export default class ThingPicker extends React.Component {
render() {
const {
initialThing,
things,
onThingChanged,
} = this.props;
const orderedThings = things.sort();
return (
<Picker
selectedValue={initialThing}
onValueChange={onThingChanged}>
{buildThingItems(orderedThings)}
</Picker>
);
}
}
function buildThingItems(orderedThings) {
let items = orderedThings.map(th => {
const it = th === "BMD" ? (<Text key={th} label={"foo"} value={"bar"}}>Hello</Text>)
: (<Picker.Item key={th} label={th} value={th} />);
return it;
});
}
Yes! It is possible, it just might not look very "right" for React/JSX code. Just create the elements you need and assign them to the label field:
function buildThingItems(orderedThings) {
let items = orderedThings.map(th => {
const it = (<Picker.Item
key={th}
label={currency === "BMD" ? (<Text>Hello</Text>) : th}
value={th} />);
return it;
});
}