I am trying to load a video into VideoJS player in a react project. The Video is returned from a web service taking specific token that authenticate the user.
Let's say the video is returned from the below API call:
localhost:8080/video/1/
In order to play this video, the user should be authenticated. In other words, the API takes the below header to return a successful result:
auth: token
My VideoJs player is built in a React component as below:
import React from 'react'
import videojs from 'video.js'
export default class VideoComponent extends React.Component {
componentDidMount () {
this.videoJsOptions = {
sources: [{
src: 'http://localhost:8080/video/1/',
type: 'video/mp4',
}],
}
let player = videojs(this.videoNode, this.videoJsOptions, function onPlayerReady() {
console.log('onPlayerReady', this)
})
this.player = player
}
render () {
return (
<div data-vjs-player>
<video ref={node => this.videoNode = node} className="video-js"></video>
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
How is it possible to let my video component take the token of the calling URL and pass it the request header?
I would put this component as a child of another component whose sole responsibility is to make the API call and render the VideoComponent if the user is authorized. Something along these lines.
You'll probably want some type of redirect or error message feedback to the user if they are not authorized. I did not incorporate that in my code snippet.
export default class VideoAuth extends React.Component {
state = {
isAuthorized: false
}
async componentDidMount() {
const authRequest = await someAuthRequest()
if (authRequest.ok) {// or however the data structure looks like
this.setState({ isAuthenticated: true })
}
}
render() {
return this.state.isAuthenticated ? <VideoComponent /> : <div> Authorizing... </div>
}
}
Related
My react app doesnt show my api calls. At least some of them.
I would like to make sure my code does wait for the api call to resolve.
I saw a tutorial here https://codewithnico.com/react-wait-axios-to-render/ where they use a functional component with if isLoading to show loading status and useEffect to make the api call and wait for the results.
I have a class component and I would like to check if the use of componentDidMount is indeed waiting for the api call to resolve like useEffect is in the functional tutorial i saw
Here is the code:;
import React from "react";
import axios from "axios";
import { StreamField } from "./StreamField/StreamField";
class PostDetail extends React.Component {
constructor(props) {
super(props); this.state = {
post: [],
loading: true, };
}
componentDidMount() {
const pk = this.props.match.params.id;
axios.get(`/api/cms/pages/${pk}/`).then((res) => {
const post = res.data;
this.setState({
post,
loading: false });
}) }
render() {
if (!this.state.loading) {
const post = this.state.post;
return (
<div className="col-md-8">
<img src={post.header_image_url.url} className="img-fluid rounded" alt=""/>
<hr />
<h1>{post.title}</h1>
<hr />
<StreamField value={post.body} />
</div> );
}
else {
return <div className="col-md-8">Loading...</div>;
}
}
}
export { PostDetail };
What do you think? Is my code in cause for my component not loading properly ( the delay to resolve is quite long on my test machine : several seconds)
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!
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;
I have a 1-year React application that uses Server-Side Rendering and now we're developing a page that will be indexed by Googlebot.
The problem is: we need the response of an async api call to render a page within that data for SEO purposes. Googlebot (view-page-source) must not have a Loading... component or anything else. Its content MUST be that api data.
However all the examples/solutions that I found about it told to use componentDidMount, componentWillMount (deprecated) or even constructor, but all of them renders the page without the data first and Googlebot won't wait for it to finish.
Example:
API response:
{ trip: { description: 'It was great', title: 'The Trip! 2.0' } }
The component:
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
}
}
render() {
return (
<div>
{
this.state.data ? <h1>{this.state.data.title}</h1> : <p>none</p>
}
</div>
);
}
};
export default HomePage;
Desired source code on all renders:
<h1>The Trip! 2.0</h1>
NEVER: <p>none</p>
PS: the api call is not being simulated on this example cause idk where to put it =x
What could I do to solve this problem considering that the component must NOT render without the api response? Is that possible? Thank you all!
Inside of each component you have to define a function, let's name it as loadData. this function will do async work of your component.
when your server gets a request, you have to look at that Url and then you have to decide which components to render.
For each component that needs to be rendered, we will call that loadData function that is attached to each of components to initiate the data loading process. The key here is that we are not doing some initial render of the application. We just have a set of components. Each one says “here is a resource that I need”. Then whenever someone makes a request, we look at the set of components that we should need to render to get the page to show up. Then we will take all those components, we will take these little data loading requirement functions that are attached to them and we will call each of those. All these dataLoad functions return promise, so we have to detect all of them resolved before we render our app.
Let's say we have Users.js so we define our function as follow:
const loadData = store => {
return store.dispatch(fetchUsers());
};
when we import our component, we import inside an object.
export default {
loadData:loadData,
component: connect(mapStateToProps, { fetchUsers })(UsersList)
};
We have to set up our Routes.js file with the help of react-router-config.
Routes.js
import React from "react";
import Home from "./pages/Home";
import Users from "./pages/UsersList";
import App from "./App";
import NotFoundPage from "./pages/NotFoundPage";
export default [
{
...App,
//App component will be shown inside each component.
//array of routes will be used inside the App.js down below
routes: [
{
path: "/",
...Home,
exact: true
},
{ ...UsersList, path: "/users" },
{ ...AdminsListPage, path: "/admins" },
{ ...NotFoundPage }
]
}
];
Here is the App.js
import React from "react";
import Header from "./components/Header";
import { renderRoutes } from "react-router-config";
import { fetchCurrentUser } from "./actions";
//this component is to render components that each component uses in common
//props.route is the child components that are passed from Routes.js
const App = ({ route }) => {
return (
<div>
<Header />
{renderRoutes(route.routes)}
</div>
);
};
export default {
component: App,
//this function is get called by redux so store is passed in
//as you might know Header should always know about authentication status
//we populate the store with the authentication info so Header can use it.
loadData: ({ dispatch }) => dispatch(fetchCurrentUser())
};
now we set up all the loadData functions, now it is time to invoke them when our server gets request. for this we will be using matchRoutes function from react-router-config.
app.get("*", (req, res) => {
const store = createStore(req);
//express does not touch routing. it delegates everything to react.
//I will just place the logic behind invoking loadData functions here.
//matchRoutes will look at the path and will return the array of components to be loaded.
//this is what matchRoutes function show `{ route: { loadData: [Function: loadData], path: '/users', component: [Object] },//component that we show
match: { path: '/users', url: '/users', isExact: true, params: {} } }
`
//
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
}) //we got the array of loadData functions now we are invoking them
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
//Promise.all takes an array of promises and resolves when all of the items resolve
Promise.all(promises).then(()=>{
//here is when it is time to render your server-side code.
}).catch(e => console.log(e.message));
}
We have hosted a bot on ServiceNow and would now like to pass attributes from the browser to the BOT. How can I make this happen?
This question is actually part 2 of a question I had posted & which I have already found a solution for.
Since the BOT is already logged into ServiceNow. I want to extract some elements from the background/servicenow page source and pass it to the react app as shown below. The BOT authenticates the user by email so it would act like a SSO because he is already connected to ServiceNow with the same email id. We therefore want to simply pass that value.
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
setTimeout(() => {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: {
language: window.navigator.language,
userid: "a.b#c.d",
username: "a.b#c.d"
}
}
});
}, 1000);
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
if (action.payload.activity.from.role === 'bot') {
this.setState(() => ({ newMessage: true }));
}
}
return next(action);
});
You can pass the data from your page to your bot by dispatching an event listener on the page and catching the event in the web chat implementation.
In this example, I am simulating a user having logged in with a button click. The button click creates the event. When the event is registered, it is picked up by web chat which then takes the values stored in window.NOW.user and forwards that data to the bot. To help drive the point home, I am sending a message greeting the user by name while also sending the data (name and email) behind the scenes.
Hope of help!
app.js: Imports the view for display.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import WebChatView from './webChatView';
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Route path="/" exact component={WebChatView} />
</div>
</Router>
);
}
}
export default App;
webChatView.js: I import the webchat component into the view and create a function that, on click (again, just to simulate someone having logged in), creates and dispatches an event.
import React, { Component } from 'react';
import WebChat from './webchat';
class WebChatView extends Component {
constructor(props) {
super(props);
this.sendToBot = this.sendToBot.bind(this);
}
render() {
return (
<div>
<div>
<WebChat id="webchat" role="main" />
</div>
<div>
<button id="loginBtn" onClick={this.sendToBot}>Login</button>
</div>
</div>
)
}
sendToBot = () => {
let sendToBotEvent = new Event('sendToBot')
window.dispatchEvent(sendToBotEvent);
window['NOW'] = {
'user': {
'name': 'John Doe',
'email': 'john.doe#somedomain.com'
}
}
}
}
export default WebChatView;
webchat.js: Lastly, is web chat, itself. I create a store and an event listener. When the event is dispatched in the window, the message/data is also dispatched to the bot. Because I'm simulating logging in as one step, I have included a setTimeout so the window.NOW.user data has a chance to save. The store is passed to <ReactWebChat> which, subsequently, sends the associated data to the bot for processing.
import React from 'react';
import ReactWebChat, { createDirectLine, createStore, } from 'botframework-webchat';
export default class WebChat extends React.Component {
constructor(props) {
super(props);
const store = window.WebChat.createStore();
window.addEventListener('sendToBot', () => {
setTimeout(() => {
store.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'Service Now user name',
value: window.NOW.user
}
})
store.dispatch({
type: 'WEB_CHAT/SEND_MESSAGE',
payload: {
text: `Hi ${window.NOW.user.name}!`
}
})
}, 300)
})
}
this.state = {
store: store
};
componentWilUnmount() {
window.removeEventListener('sendToBot', null)
}
render() {
return (
this.state.directLine ?
<ReactWebChat
directLine={this.state.directLine}
store={this.state.store}
/>
:
<div>Connecting to bot…</div>
)
}
}