ReactJS update view after receiving new props - reactjs

After installing ReactJS again after a few months not working with it, I noticed the latest version (16) is now using getDerivedStateFromProps and there is no more will receive props functions and stuff.
Currently I have my environment running with react-redux included. My new data gets into the mapStateToProps function of my container script, but I want to update the view accordingly. Basically a loading screen, and after the data is fetched via an API call, update the view with the API's response data.
However, I don't seem to be able to find a solution to update my view anywhere up till now.
I noticed that the getDerivedStateFromProps only gets triggered once.
Am I missing some functions or anything?
Short example:
import React from 'react';
import { connect } from "react-redux";
import Files from '../components/files';
class ProjectContainer extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.getFilesByShare('sharename');
}
componentDidUpdate (prevProps) {
console.warn('does not get here?');
}
render() {
const { loading, files } = this.props;
let content = (
<div className="loading">Loading... Requesting file urls</div>
);
if (!loading && files && files.length) {
content = (
<div>
File urls requested!
<Files files={files} />
</div>
);
}
return (
{content}
);
}
}
const mapStateToProps = state => {
console.warn(state, 'this shows the new data');
return {
files: state.files,
loading: state.files_loading,
};
};
export default connect( mapStateToProps, {
getFilesByShare,
}) (ProjectContainer);

Related

How to access naviagtion options from imported file in react-native

I'm passing data through different pages down to the last page in my app, its been working fine.
But the issue is the last page has 2 components so the typical </ChatActivity navigation="{this.props.navigation}" />, here's what I mean:
I have an App.js
content of App.js
import ChatScreen from './chat'
class ChatActivity extends Component {
static navigationOptions = {
...
}
render() {
return(
<ChatScreen navigation={this.props.navigation} />
)
}
}
I also have chat.js that contains the chat component. Chat.js itself, needs to import Fire from './fire.js'
so now, this.props.navigation was only passed to Chat.js...but I need to access it from fire.js as well.
I've read about import {useNavigation}, but from what i have tried it didn't work cause my fire.js doesn't even look like the example in the docs
this is my fire.js
class Fire extends React.Component{
constructor (props) {
super(props)
this.init()
this.checkAuth()
}
init = () => {
firebase.initializeApp({
})
};
checkAuth = () => {
firebase.auth().onAuthStateChanged(user => {
if (!user) {
firebase.auth().signInAnonymously();
}
})
}
send = messages => {
messages.forEach(item => {
const message = {
text: item.text,
timestamp: firebase.database.ServerValue.TIMESTAMP,
// image: item.image,
//video: item.video,
user: item.user
}
this.db.child(`NEED NAVIGATION PARAMS HERE`).push(message)
})
}
parse = message => {
const {user, text, timestamp} = message.val();
const {key, _id} = message
const createdAt = new Date(timestamp)
return {
_id,
createdAt,
text,
user
}
}
get = callback => {
this.db.child(`NEED NAVIGATION PARAMS HERE`).on('child_added', snapshot => callback(this.parse(snapshot)))
}
off() {
this.db.off()
}
get db() {
return firebase.database().ref(`NEED NAVIGATION PARAMS HERE`);
}
get uid(){
return(firebase.auth().currentUser || {}).uid
}
}
export default new Fire();
Since i couldn't access navigation params, I tried AsyncStorage, but thats probably not the best practice and it isn't working too well. Not sure if its the AsyncStorage or react-native-gifted-chat but when I load the chat page once, it shows the same messages for other chats till I restart the app which shouldn't be cause i'm fetching the data based on unique parameters.
You have just missed one step here...
Since you have passed the navigation as props by using the following approach:
<ChatScreen navigation={this.props.navigation} />
the chat screen gets to use navigation properties of ChatActivity.
For Fire.js to be able to use the navigation as well, that was provided to Chat.js by ChatActivity you will need to pass the navigation props received by Chat.js to Fire.js in the same way.
This is how your Chat.js should look like:
import Fire from './Fire'
class Chat extends Component {
static navigationOptions = {
...
}
render() {
return(
<Fire navigation={this.props.navigation} />
)
}
}
That should solve the issue. Cheers!

React - what are the steps to get data from api and render it?

I am building a site just like stackoverflow.com. I want my home page to display top questions. For that, I have sample questions on the backed. Now, I want to display only the question and tags from the questions array.
The code is in the image
I have made axios connection for that:
const instance = axios.create({
baseURL: "https://2w2knta9ag.execute-api.ap-south-1.amazonaws.com/dev", });
instance.defaults.headers.post["Content-Type"] = "application/json";
To connect it, I wrote the command: instance.get("/questions)
Now, how do I display only the question and tags??
EDIT:
On using the code given bellow, my js file now becomes:
import React from 'react';
import instance from '../../api';
class QuestionList extends React {
componentDidMount() {
instance
.get("/questions")
.then((res) => {
this.setState({ data: res.data });
});
}
render () {
const { data } = this.state;
return <div>
{
data && data.map(d => {
return <div>question: {d.question}, tags: {d.tags}</div>;
})
}
</div>
}
}
export default QuestionList;
But, this is just making my site in a loading state, and it gets hanged!!
If I understood correctly, you want to get an array only with the tags and the question. if so, you can use Array.prototype.map for this
const questions = result.map(({ question, tags }) => ({ question, tags }))
First you export the axios instance so that it can be used from other components.
Now you can send the api request in componentDidMount and update your component's state with the data.
And in render function, you just get the value from state and display.
If you are new to react, learn React Hooks and know that componentDidMount method is the best place to send api requests.
For Example:
import React from 'react';
import instance from '../../api';
class QuestionList extends React.Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
instance.get('/questions').then((res) => {
this.setState({ data: res.data });
});
}
render() {
const { data } = this.state;
return (
<div>
{data &&
data.map((d) => {
return (
<div>
question: {d.question}, tags: {d.tags}
</div>
);
})}
</div>
);
}
}
export default QuestionList;

GraphQL+Apollo+react:Unable to display data

Below is the sample client side code using Apollo client.
I am providing data from nodemon express server.
My query works fine in graphiQL.
import React from "react"
import { graphql } from 'react-apollo'
import gql from "graphql-tag";
const CounterpartyQuery = gql`
query data{
counterparty {
name
}
}
`;
export class ExchangeRates extends React.Component {
render() {
console.log(this.props.data) // Giving undefined
return (
<div>hiii{this.props.data.counterparty.map(x => x.name)}</div> //Error
)
}
}
const counterpartyData = graphql(CounterpartyQuery, { name: 'data' })(ExchangeRates)
export default counterpartyData
This is happening because your network call isn't over but the component gets rendered using the variable not available.
You need to use networkStatus to see whether the network call is over only then the data would be available to render. Until then you need to show a loader.
So replace
graphql(CounterpartyQuery, { name: 'data' })
by
graphql(CounterpartyQuery, {
name: 'data' ,
props : ({data}) => {
return {
data,
isLoading:data['networkStatus']==1 || data['networkStatus']==2 || data['networkStatus']==4
}
}
}),
and then use
this.props.isLoading
variable in the render method to decide whether to show loading or to show the list.
For eg:
render() {
console.log(this.props.data, this.props.isLoading)
return (
{this.props.isLoading ? <div>Loading Data</div>:<div>hiii{this.props.data.counterparty.map(x => x.name)}</div>
}
)
}
https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-networkStatus
Apollo client HOC passes a loading prop to each of your queries, you'll need it in almost every case in order to wait for the response before rendering your data (you also need to handle request errors):
export class ExchangeRates extends React.Component {
render() {
if (this.props.data.loading) {
return <div> Loading </div>; // rendering an animated loading indicator here is good practice to show an activity
}
if (this.props.data.error) {
return <div> Error </div>;
}
return (
<div>hiii{this.props.data.counterparty.map(x => x.name)}</div>
)
}
}

React file upload how to show toast on upload success or failure

I have a file upload component as follows:
import React from 'react';
import ReactDOM from 'react-dom';
export default class FileUploadForm extends React.Component{
render (){
return (
<div class="row">
<div class="file-field input-field">
<div class="btn">
<span>Browse</span>
<input type="file" multiple onChange={this.onChange.bind(this)} ref = "file"/>
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Upload multiple files" />
</div>
</div>
</div>
);
}
onChange(e){
console.log("FileUploadForm: upload files selected")
let files = e.target.files;
if(files.length == 0){
return
}
console.log(files[0]);
this.props.onFileUploadRequested(files);
}
}
and here is the container to connect it to redux:
import { connect } from 'react-redux';
import { uploadFiles } from '../actions.js';
import FileUploadForm from '../components/FileUploadForm';
const mapStateToProps = (state, ownProps) => {
return {
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onFileUploadRequested: (files) => {
dispatch(uploadFiles(files));
}
}
}
const FileUploadContainer = connect(
mapStateToProps,
mapDispatchToProps
)(FileUploadForm);
export default FileUploadContainer;
I am using materialize CSS which has a Toast component.
// Materialize.toast(message, displayLength, className, completeCallback);
Materialize.toast('I am a toast!', 4000) // 4000 is the duration of the toast
I hit an API to upload images which tells if the call failed or succeeded. Then I dispatch actions which change state field (fileUploadStatus)as:
FILE_UPLOAD_SUCCEEDED: fileUploadStatus = SUCCESS
FILE_UPLOAD_FAILED: fileUploadStatus = FAILURE
the initial value of this field is READY.
I am not sure how to show toast on upload success or failure.
Following are my queries:
Is it advisable to use a global message component and show all such messages of API call success or failure in that or something inside file upload component only?
Considering the use of field 'uploadStatus' in stated in above, how do I revert it to READY state so that the message is not shown on further render?
I am a complete beginner to developing single page applications and have only working knowledge of javascript. I am stuck on this on for quite some time now. Please suggest if you feel there are some gaps in my knowledge.
1) There are a number of ways to solve this. What has worked for me is to expose a toast function via context, that lives in the top-most component. In React, context should be used sparingly but this scenario is acceptable IMO. For example:
class App extends Component {
toast() {
...
}
getChildContext() {
return {
toast: this.toast
};
}
render() {
return (
<Child />
);
}
}
App.childContextTypes = {
toast: PropTypes.func
};
class Child extends Component {
...
onSomeEvent() {
this.context.toast('Something happened.');
}
}
Child.contextTypes = {
toast: PropTypes.func
};
2) Materialize.toast() has an optional callback function as a 4th argument which you can use however you want. In this case, it can be used to revert your fileUploadStatus.

Meteor loading data with React Komposer

I'm trying to load data using React Komposer and I'm not sure what I'm doing wrong, pretty sure this is the way it should be unless I miss something. But I'm not getting any data in the UI. Could use the help
container.js
import { composeWithTracker } from 'react-komposer';
import RightNavBar from './right-nav-bar.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
const currentUser = 'bbbb';
onData(null, currentUser);
}
export default composeWithTracker(composer)(RightNavBar);
My component
export class RightNavBar extends React.Component {
render() {
return (
<div>
aaaa {currentUser}
</div>
);
}
}
Here is the "standard" example from react-komposer's repository (adapted to your specific case)
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
if (subscription.ready()) {
const currentUser = Meteor.user(); //or whatever
onData(null, {currentUser});
};
};
Here you subscribe and when the subscription is ready, your component is rendered. Otherwise, a loading component is rendered.
The 2nd parameter to onData should be an object. It is merged with other props passed to your component and is accessible from within your component via this.props.
From within your component,the props object is available via this.props, so you can either deconstruct it or access its properties directly.
class RightNavBar extends React.Component {
render() {
const {currentUser} = this.props;
return (
<div>
Hello, {currentUser.name}!
</div>
);
}
}
Your code sends a string rather than an object and React has no way of making sense of the token currentUser from within your component.

Resources