Currently i have an object from the FireBase database. Like so:
I want to render this data using React for now i have:
return (
<React.Fragment>
<StyledMain role="main">
<Layout currentUser={currentUser}>
<ListCards currentUser={currentUser} />
{data.map((item, index) => (
<Card id={Object.keys(item).toString()} key="c" type="chats">
<CardContent scrollbar={false}>
<Grid columns={`${rem(300)} 1fr`} scrollbar={false}>
{Object.keys(item).map(function(key, index) {
<Text text={item[key]['type'].toString()} />;
})}
</Grid>
</CardContent>
</Card>
))}
</Layout>
</StyledMain>
</React.Fragment>
);
Having data containing the firebase Widgets object.
My goal is to render Card where the id is erf4553o459g4 and the Text component will render lists
What is the best approach to accomplish this? Two map functions sounds a bit unnecessary.
The function to get the data from FireBase:
const getSingleRefFunction = (ref, callback) => {
const dataArray = [];
ref.once('value', snap => {
dataArray.push(snap.val());
callback(dataArray);
});
};
Related
I'm new to react and I'm having trouble getting my head around the architecture of my app. Here's what I've got:
I use fetch() to get an array of records (records) from the backend. I use a couple of custom components to handle outputting the data. At the end of the tree is a series of touchable components which I need to open a single modal component on press. Here's a simple version of the code:
./app.js
<ScrollView>
{records.map((record, index) => {
return <Post key={index} post={record} />
})}
</ScrollView>
./src/components.js
function Post(props) {
return (
<Child info={props.post} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => ...open modal...}>{props.info}</TouchableHighlight>
)
}
So I can't put my <Modal> in either of the components in components.js, as then I end up with one modal for each record in the database. So I figure I need to put it at the end of the <ScrollView> in app.js, but then how do I open and close it from the <Child> component? I've read that it's possible to pass info back up the chain to a parent component using a callback function, but I'm unsure of how to do that while also passing props down that contain the information of each record.
You can try this logic:
const [modalVisible, setModalVisible] = useState(false);
const handleVisibility = () => {
setModalVisible(!modalVisible)
}
return(
<View>
<YourWrappingModalComponent visible={modalVisible} />
<ScrollView>
{records.map((record, index) => {
return <Post
key={index}
post={record}
handleVisibility={handleVisibility} />
})}
</ScrollView>
</View>
)
To open modal from Child you do:
function Post(props) {
return (
<Child info={props.post} handleParentModal={props. handleVisibility} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => props.handleParentModal()}>{props.info}</TouchableHighlight>
)
}
Using a component like YourWrappingModalComponent where you define your modal is very useful if you use your modal in many places.
Can't use React Reveal on array of data with .map() to produce effect from documentation.
https://www.react-reveal.com/examples/common/
Their documentation gives a nice example
<Fade left cascade>
<div>
<h2>React Reveal</h2>
<h2>React Reveal</h2>
<h2>React Reveal</h2>
</div>
</Fade>
I want to produce the same CASCADE effect with my data
<React.Fragment>
{projects.filter(project => project.category === category)
.map((project, index) => {
return (
<ProjectThumb key={index} project={project}
showDetails={showDetails}/>
)
})}
</React.Fragment>
The effect I'm getting is that the entire ProjectThumb component list fades in in one group, I need them to fade in individually and as i scroll. Thanks in advance.
Pass react-reveal props to your React component. It will work.
<Fade left cascade>
<div>
{
projects
.filter(project => project.category === category)
.map((project, index) => (
<ProjectThumb key={index} project={project} showDetails={showDetails} />
))
}
</div>
</Fade>
In your ProjectThumb.js
const ProjectThumb = props => {
return <Whatever {...props}>{...}</Whatever>
}
I'm using React-virtualized to show the data as list.
I add selected styling for list item, it supposes highlight the item once it got click.
The current problem is onClick is fired, but selcted styling only shows when scroll up the list.
List component
<div className={styles.autoSizerContainer}>
<AutoSizer>
{({width, height}) => {
// Selected customer styling only fire after scroll
return (
<List
width={width}
height={height}
rowHeight={50}
rowRenderer={this.renderRow}
rowCount={rowCount}
overscanRowCount={3}
className={styles.list}
/>
)
}}
</AutoSizer>
</div>
List item
private renderRow = ({index, key, style}: ListRowProps) => {
const data = this.props.dataList[index];
return (
<div style={style} key={data.id}>
<div className={styles.listItem}>
<div>data.name</div>
<Item key={data.id}
isDataSelected={this.state.selectedId === data.id}
/> //return true will show selected styling
</div>
</div>
)
};
"react-virtualized": "^9.21.0",
"react": "^16.8.4"
Any ideas are welcome!
Thanks!!!
React-Virtualized will only re-render your component when one of the props provided by ListRowProps changes. It doesn't know that your render method uses this.props.dataList and this.state.selectedId internally. You will need to do one of two things.
Explicitly tell the List to redraw when this.state.selectedId changes. The List exposes an api for this purpose.
Use something like React's Context api to provide the necessary data in a way such that changes can be detected. Something like this should work:
const {Provider, Consumer} = React.createContext<number | null>(null);
<div className={styles.autoSizerContainer}>
<AutoSizer>
{({width, height}) => {
// Selected customer styling only fire after scroll
return (
<Provider value={this.state.selectedId}>
<List
width={width}
height={height}
rowHeight={50}
rowRenderer={this.renderRow}
rowCount={rowCount}
overscanRowCount={3}
className={styles.list}
/>
</Provider>
)
}}
</AutoSizer>
</div>
private renderRow = ({index, key, style}: ListRowProps) => {
const data = this.props.dataList[index];
return (
<Consumer>
{(selectedId) =>
<div style={style} key={data.id}>
<div className={styles.listItem}>
<div>data.name</div>
<Item key={data.id}
isDataSelected={selectedId === data.id}
/> //return true will show selected styling
</div>
</div>
}
</Consumer>
)
};
I'm currently building app and am using the firebase for data storage, and am having an issue spreading the same data to the two different components( for example: AllProjects and SelectedProject) once the user is logged in.
In the first component(AllProjects), it works and am able to map through the data array and build cards that I want to use for navigating to the second component(SelectedProject). But when I map the same data array again, to spread its content to SelectedProject component(to each individual project), the map is not working and only the data from the first project in the array is being passed to each other project.
function UserPanel(props) {
const [projects, setProjects] = useState([]);
//get data from firebase
useEffect(() => {
return db
.collection("users")
.doc(`${props.user.uid}`)
.collection("projects")
.onSnapshot(snapshot => {
const docs = [];
snapshot.forEach(doc => {
docs.push({
...doc.data()
});
});
setProjects(docs);
});
}, [props.user.uid]);
return (
<div>
<Nav user={props.user} />
<Router>
<AllProjects projects={projects} path="/" />
{projects.map(project => (
<SelectedProject
project={project}
path="projects/:projectId"
key={project.id}
/>
))}
</Router>
</div>
);
}
export default UserPanel;
First Component
function AllProjects(props) {
return (
<div>
{props.projects.map(projects=> (
<Link key={projects.id} to={`/projects/${projects.id}`}>
<ProjectCard projects={projects} />
</Link>
))}
</div>
);
}
I was not able to find the solution for spreading all data dynamically through the app(even not sure now if it's possible?) but rather making a second API call from inside the second component and using id from the router path to select the desired project.
Im trying to pass parameters through to a component two levels deep.
I have 2 screens (MainScreen & UserProfileScreen) with a flat list on both screens, both flat lists use the same component EventCard in its renderItem. EventCard is made up of 3 three nested components EventCardHeader,EventCardBody & EventCardFooter. How do I pass certain arguements only from the UserProfileScreen? I have posted code below to give a better understanding of what I have.
MainScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
/>
openEditEvent = (event) => {
this.props.navigation.navigate('EventFormScreen', {
event: event,
eventKey: this.state.eventKey,
editMode: true,
});
};
EventCard
export default class EventCard extends Component {
render() {
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent={() => this.openEditEvent()}
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
};
EvenCardHeader
export default class EventCardHeader extends Component {
render() {
return (
<CardSection style={styles.eventCardHeader}>
<Thumbnail small
style={styles.eventOrganiserImage}
source={{uri: this.props.eventOrganiserImage}}/>
<Text style={styles.eventPromoterName}>{this.props.eventVenue}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() => this.props.openEditEvent()}>
Edit
</Button>
}
{!!this.props.openDeleteEventAlert &&
<Button onPress={() => this.props.openDeleteEventAlert()}>
Delete
</Button>
}
</CardSection>
);
}
}
I can see that because I have hardcoded the this.openEditEvent() function into my EventCard component that what's causing me the problem, because then the if statement in EventCardHeader that checks if the this.openEditEvent() exists always evaluates to true. Would someone be able to help show me the right way to do this? Thanks in advance for any help.
UPDATE:
Added in openEditEvent
Where is openEditEvent() declared? It should be in the parent component and passed as props to whatever children you need it in. You can continue to pass it as props from children to children.
EDIT:
Ok so you can pass openEditEvent as props like so:
<EventCard
openEditEvent = this.openEditEvent
openEventDetail={() => this.openEventDetail(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
That function can be available in EventCard, and can then be passed AGAIN as props to another child component:
render() {
var openEditEvent = this.props.openEditEvent;
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent = openEditEvent
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}