I have video that I'd like to present as preview_video image/thumbnail before a user clicks on them for the full video. Is there a RN component to do this?
I have this code:
import { Video } from 'expo-av';
import VideoPlayer from 'expo-video-player';
import * as React from "react";
import {View} from "react-native";
import Header from "../../components/header/Header";
export default function VideoPlayerComponent(props) {
const { url } = props.route.params;
return (
<View>
<Header
title="Video"
goBack
/>
<VideoPlayer
style={{height:550}}
videoProps={{
shouldPlay: true,
resizeMode: Video.RESIZE_MODE_CONTAIN,
source: {
uri: url,
},
}}
/>
</View>
);
}
Seems you can try this component: https://docs.expo.dev/versions/latest/sdk/video-thumbnails/
const generateThumbnail = async () => {
try {
const { uri } = await VideoThumbnails.getThumbnailAsync(
'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
{
time: 15000,
}
);
setImage(uri);
} catch (e) {
console.warn(e);
}
Then just include a condition when the video component is in its paused state, the thumbnail should be displayed with a higher zIndex.
Like describe here: react native video display thumbnail before video is played
I'm learning react native by creating some projects.
I got stuck and unable to load custom fonts. I've followed this guide from expo docs. but I'm getting an error
fontFamily "raleway-bold" is not a system font and has not been loaded
through Font.loadAsync.
Then I have use this Font.loadAsync in my code but I still get the same error.
Any suggestions?
import React, { useState } from 'react';
import Home from './screens/Home';
import AppLoading from 'expo-app-loading';
import * as Font from 'expo-font';
const getFonts = () =>
Font.loadAsync({
'raleway-regular': require('./assets/fonts/Raleway-Regular.ttf'),
'raleway-bold': require('./assets/fonts/Raleway-Bold.ttf'),
});
export default function App() {
const [fontsLoaded, setFontsLoaded] = useState(false);
if (fontsLoaded) {
return <Home />;
} else {
return (
<AppLoading
startAsync={getFonts}
onFinish={() => setFontsLoaded(true)}
onError={console.warn}
/>
);
}
}
This is an expo client and AppLoading bug GitHub issue
You can try an alternative approach and not use AppLoading as below
useEffect(() => {
async function getFonts() {
await fetchFonts();
setFontLoaded(true);
}
getFonts();
}, []);
if (!fontLoaded) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
Problem:
My App component is giving me an error like this.
D:\projects\myapp\node_modules\react-native\Libraries\Core\ExceptionsManager.js:173 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in App (at renderApplication.js:45)
This is how my code looks like.
/* eslint-disable prettier/prettier */
import React, {Component} from 'react';
import {StatusBar} from 'react-native';
import {Provider} from 'react-redux';
import RootNavigator from '_navigations/RootNavigator';
import AsyncStorage from '#react-native-community/async-storage';
import store from '_store';
import Splashscreen from '_screens/splashscreen';
import axios from 'axios';
import refreshToken from '_store/actions/refreshToken';
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
this.updateLoading();
refreshToken();
}
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (data !== null) {
this.setState({isLoading: false});
}
};
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve('result');
}, 3000),
);
};
render() {
if (this.state.isLoading) {
return <Splashscreen />;
}
return (
<>
<StatusBar barStyle="dark-content" />
<Provider store={store}>
<RootNavigator />
</Provider>
</>
);
}
}
export default App;
My splash screen is like this.
import React from 'react';
import {View, Image} from 'react-native';
import styles from './splashstyle';
import logo from '_assets/img/logo.png';
const Splashscreen = () => {
return (
<View style={styles.container}>
<Image source={logo} style={styles.logo} />
</View>
);
};
export default Splashscreen;
As it is saying the issue is inside rendered method but I do not where I have done wrong. I tried a lot to find out what where I have done wrong but it is giving me this issue over and over. Can someone help me to solve this issue?Thank you
if performTimeConsumingTask take so much time that the user moved to another screen(aka component) then the function updateLoading will continue its process which will try to setState in an unmounted component..
so you should cancel your work in componentWillUnmount to fix the issue
I would suggest some tweak like
class MyComponent{
constructor(){ ... this.isMounted = false; }
componentDidMount(){ ... this.isMounted = true; }
componentWillUnmount() { ... this.isMounted = false }
// then inside updateLoading
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (this.isMounted && data !== null) {
// will protect set state against unmounted component
this.setState({isLoading: false});
}
};
}
I have an Authentication screen like this :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Profile from './Profile';
import LoginOrSignup from './LoginOrSignup';
class Auth extends Component {
render() {
if (this.props.isLoggedIn) {
return <Profile />;
} else {
return <LoginOrSignup />;
}
}
}
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn
};
}
export default connect(mapStateToProps)(Auth);
On my LoginOrSignup screen i have a submit button contains a function to navigate like this:
this.props.navigation.navigate("ConfirmOTP");
But I always got this error :
undefined is not an object(evaluating 'this.props.navigation.navigate')
Then I tried to changed my Authentication screen like this
.....
<LoginOrSignup navigation={this.props.navigation}/>
.....
The error is gone, but it doesn't navigate to the next screen.
I'm still learning. Any helps would be really appreciated.
you need to register your sreen to a Route. This can be an example using createStackNavigator of react-navigation.
import ConfirmOtp from "./ConfirmOtp";
import { createStackNavigator } from "react-navigation";
class LoginOrSignup extends Component {
render() {
return (
<View>
<Button onClick={() => this.props.navigation.navigate("ConfirmOtp")}/>
</View>
)
}
}
export default createStackNavigator({
LoginOrSignup {
screen: LoginOrSignup
},
ConfirmOtp: {
screen: ConfirmOtp
}
});
All the examples I've seen of the new Context API in React are in a single file, e.g. https://github.com/wesbos/React-Context.
When I try to get it working across multiple files, I'm clearly missing something.
I'm hoping to make a GlobalConfiguration component (the MyProvider below) create and manage the values in the context, ready for any child component (MyConsumer below) read from it.
App.js
render() {
return (
<MyProvider>
<MyConsumer />
</MyProvider>
);
}
provider.js
import React, { Component } from 'react';
const MyContext = React.createContext('test');
export default class MyProvider extends Component {
render() {
return (
<MyContext.Provider
value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider >
);
}
}
consumer.js
import React, { Component } from 'react';
const MyContext = React.createContext('test');
export default class MyConsumer extends Component {
render() {
return (
<MyContext.Consumer>
{(context) => (
<div>{context.state.somevalue}</div>
)}
</MyContext.Consumer>
);
}
}
Unfortunately that fails with this in the console:
consumer.js:12 Uncaught TypeError: Cannot read property 'somevalue' of undefined
Have I completely missed the point? Is there documentation or an example of how this works across multiple files?
I think the problem that you are running into is that you are creating two different contexts, and trying to use them as one. It is the Context created by React.createContext that links Provider and Consumer.
Make a single file (I'll call it configContext.js)
configContext.js
import React, { Component, createContext } from "react";
// Provider and Consumer are connected through their "parent" context
const { Provider, Consumer } = createContext();
// Provider will be exported wrapped in ConfigProvider component.
class ConfigProvider extends Component {
state = {
userLoggedIn: false, // Mock login
profile: { // Mock user data
username: "Morgan",
image: "https://morganfillman.space/200/200",
bio: "I'm Mogranโso... yeah."
},
toggleLogin: () => {
const setTo = !this.state.userLoggedIn;
this.setState({ userLoggedIn: setTo });
}
};
render() {
return (
<Provider
value={{
userLoggedIn: this.state.userLoggedIn,
profile: this.state.profile,
toggleLogin: this.state.toggleLogin
}}
>
{this.props.children}
</Provider>
);
}
}
export { ConfigProvider };
// I make this default since it will probably be exported most often.
export default Consumer;
index.js
...
// We only import the ConfigProvider, not the Context, Provider, or Consumer.
import { ConfigProvider } from "./configContext";
import Header from "./Header";
import Profile from "./Profile";
import "./styles.css";
function App() {
return (
<div className="App">
<ConfigProvider>
<Header />
<main>
<Profile />
</main>
<footer>...</footer>
</ConfigProvider>
</div>
);
}
...
Header.js
import React from 'react'
import LoginBtn from './LoginBtn'
... // a couple of styles
const Header = props => {
return (
... // Opening tag, etc.
<LoginBtn /> // LoginBtn has access to Context data, see file.
... // etc.
export default Header
LoginBtn.js
import React from "react";
import Consumer from "./configContext";
const LoginBtn = props => {
return (
<Consumer>
{ctx => {
return (
<button className="login-btn" onClick={() => ctx.toggleLogin()}>
{ctx.userLoggedIn ? "Logout" : "Login"}
</button>
);
}}
</Consumer>
);
};
export default LoginBtn;
Profile.js
import React, { Fragment } from "react";
import Consumer from "./configContext"; // Always from that same file.
const UserProfile = props => {...}; // Dumb component
const Welcome = props => {...}; // Dumb component
const Profile = props => {
return (
<Consumer>
...
{ctx.userLoggedIn ? (
<UserProfile profile={ctx.profile} />
) : (<Welcome />)}
...
</Consumer>
...
Reading the source code of React-Context, they do
<MyContext.Provider value={{
state: this.state,
}}>
and
<MyContext.Consumer>
{(context) => <p>{context.state.age}</p>}
So if you do
<MyContext.Provider value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider>
You should get somevalue like that
<MyContext.Consumer>
{(context) => <div>{context.somevalue}</div>}
</MyContext.Consumer>
EDIT
What if you create a file called myContext.js with:
const MyContext = React.createContext('test');
export default MyContext;
and then import it like :
import MyContext form '<proper_path>/myContext';
As of right now, the two context you created in the files are not the same even thought the name is the same. You need to export the context that you created in one of the files, and use that through out.
so something like this, in your provider.js file:
import React, { Component } from 'react';
const MyContext = React.createContext();
export const MyContext;
export default class MyProvider extends Component {
render() {
return (
<MyContext.Provider
value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider >
);
}
}
then in your consumer.js file
import MyContext from 'provider.js';
import React, { Component } from 'react';
export default class MyConsumer extends Component {
render() {
return (
<MyContext.Consumer>
{(context) => (
<div>{context.somevalue}</div>
)}
</MyContext.Consumer>
);
}
}
I'm gonna throw my solution into the pot - it was inspired by #Striped and simply just renames the exports into something that makes sense in my head.
import React, { Component } from 'react'
import Blockchain from './cloudComputing/Blockchain'
const { Provider, Consumer: ContextConsumer } = React.createContext()
class ContextProvider extends Component {
constructor(props) {
super(props)
this.state = {
blockchain: new Blockchain(),
}
}
render() {
return (
<Provider value={this.state}>
{this.props.children}
</Provider>
)
}
}
module.exports = { ContextConsumer, ContextProvider }
Now it's easy to implement a ContextConsumer into any component
...
import { ContextConsumer } from '../Context'
...
export default class MyComponent extends PureComponent {
...
render() {
return (
<ContextConsumer>
{context => {
return (
<ScrollView style={blockStyle.scrollView}>
{map(context.blockchain.chain, block => (
<BlockCard data={block} />
))}
</ScrollView>
)
}}
</ContextConsumer>
)
}
I'm SO done with redux!
TLDR; Demo on CodeSandbox
My current method of solving the same problem is to use the Unstated library, which as a convenient wrapper around the React Context API. "Unstated" also provides dependency injection allow the creating of discrete instances of a container; which is handy for code reuse and testing.
How to Wrap a React/Unstated-Context as a Service
The following skeleton API Service holds state properties such as loggedIn, as well as two service methods: login() and logout(). These props and methods are now available throughout the app with a single import in each file that needs the context.
For example:
Api.js
import React from "react";
// Import helpers from Unstated
import { Provider, Subscribe, Container } from "unstated";
// APIContainer holds shared/global state and methods
class APIContainer extends Container {
constructor() {
super();
// Shared props
this.state = {
loggedIn: false
};
}
// Shared login method
async login() {
console.log("Logging in");
this.setState({ loggedIn: true });
}
// Shared logout method
async logout() {
console.log("Logging out");
this.setState({ loggedIn: false });
}
}
// Instantiate the API Container
const instance = new APIContainer();
// Wrap the Provider
const ApiProvider = props => {
return <Provider inject={[instance]}>{props.children}</Provider>;
};
// Wrap the Subscriber
const ApiSubscribe = props => {
return <Subscribe to={[instance]}>{props.children}</Subscribe>;
};
// Export wrapped Provider and Subscriber
export default {
Provider: ApiProvider,
Subscribe: ApiSubscribe
}
App.js
Now the Api.js module can be used as global provide in App.js:
import React from "React";
import { render } from "react-dom";
import Routes from "./Routes";
import Api from "./Api";
class App extends React.Component {
render() {
return (
<div>
<Api.Provider>
<Routes />
</Api.Provider>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Pages/Home.js:
Finally, Api.js can subscribe to the state of the API from deep within the React tree.
import React from "react";
import Api from "../Api";
const Home = () => {
return (
<Api.Subscribe>
{api => (
<div>
<h1>๐ Home</h1>
<pre>
api.state.loggedIn = {api.state.loggedIn ? "๐ true" : "๐ false"}
</pre>
<button onClick={() => api.login()}>Login</button>
<button onClick={() => api.logout()}>Logout</button>
</div>
)}
</Api.Subscribe>
);
};
export default Home;
Try the CodeSandbox demo here: https://codesandbox.io/s/wqpr1o6w15
Hope that helps!
PS: Someone bash me on the head quick if I'm doing this the wrong way. I'd love to learn different/better approaches. - Thanks!