Is there a known best way to support i18n for markdown imported into React? The documentation for the translation stuff indicates that it's in flux in Docusaurus 2; this case doesn't seem to be answered. I've found a cumbersome way of importing different markdown pages into React based on language, but I'd like to do it more efficiently.
Here's how I can do it. This requires an import statement for
each language version I want to support and then a lookup of the appropriate language version. It seems like there should be an easier way to do this.
import MDXHome from 'docs/home.mdx'
import FRHome from 'docs/fr/home.mdx'
const MDXImports = {
home: {
'/': (<MDXHome />),
'/fr/': (<FRHome />)
},
}
function Home() {
const context = useDocusaurusContext();
const {siteConfig = {}} = context;
return (
<!-- // Layout stuff here -->
{MDXImports.home[${siteConfig.baseUrl}]}
<!--// More stuff -->
);
}
Related
from nuxt 3 documentation,
https://nuxt.com/docs/getting-started/state-management
I'm told that I should never define ref outside script setup
since it will "be shared across all users visiting your website and can lead to memory leaks!"
I want to use vueuse's useBreakpoints,
https://vueuse.org/core/useBreakpoints/
I simply put them in composable and export,
and happily use them all across components.
but I see their type is globalThis.Ref
is it safe to use them as is,
or am I in big trouble as nuxt doc says?
// file: composables/useMedia.ts
import { breakpointsTailwind, useBreakpoints } from '#vueuse/core'
const breakpoints = useBreakpoints(breakpointsTailwind)
export const isDesktop = breakpoints.greaterOrEqual('lg')
export const isTablet = breakpoints.greaterOrEqual('sm') && breakpoints.smaller('lg')
export const isMobile = breakpoints.smaller('sm')
this is closely related to vue's response system
you don't need to worry about memory leaks when using compiler tools like nuxi
however here another problem is that react system cannot determine the dependencies and when to unmount. if you want to declare once and use globally use pinia otherwise use this code:
import { breakpointsTailwind, useBreakpoints } from '#vueuse/core'
export function useMedia() {
const breakpoints = useBreakpoints(breakpointsTailwind)
const isDesktop = breakpoints.greaterOrEqual('lg')
const isTablet = breakpoints.greaterOrEqual('sm') && breakpoints.smaller('lg')
const isMobile = breakpoints.smaller('sm')
return { isDesktop, isTable, isMobile }
}
and use
const { isDesktop } = useMedia()
note: your code doesn't react when changing the values. if you need response use computed
In our production applications with Vue 2.x, we have a toast component. This toast component is mounted once via a plugin (code below) and is then added to the Vue prototype making it accessible in every component instance.
This makes life a lot easier instead of having to add the toast to everywhere we use.
Vue 2.x plugin
export default {
install(vue: any, _: any) {
const root = new Vue({ render: (createElement) => createElement(Toast) });
root.$mount(document.body.appendChild(document.createElement("div")));
const toastInstance: Toast = root.$children[0] as Toast;
vue.prototype.$toast = {
show: (state: ToastState, text: string) => { toastInstance.show(state, text); },
hide: () => { toastInstance.hide(); }
};
}
Which can then be called in any component like:
this.$toast.show(ToastStates.SUCCESS, "Some success message");
I have recently started another project and would like to do something similar, except using Vue 3. Because we don't have access to this in the setup function, I can't use the same approach as before.
I have been looking into a few things, and have found a few ways of doing it, but none as a definitive best practice.
Provide / Inject:
This seems the most promising, where I can use
export const appInstance = createApp(App);
then
appInstance.provide("toast", toastComponentInstance)
which I can then inject in any components. The problem with this, is that to get it available in every component, it needs to be attached to the initial app instance, where it hasn't been created yet. Maybe I could manually mount it and pass it in (but that seems like a hack).
Composition:
I have also looked at this issue here: How to access root context from a composition function in Vue Composition API / Vue 3.0 + TypeScript? but didn't find that very useful and I had to do all types of hacks to actually gain access to the plugin. Gross code below..
export function useToast() {
const root = getCurrentInstance();
const openToast: (options: ToastOptions) => void = (options: ToastOptions) => {
root.ctz.$toast.open(options);
}
const closeToast: () => void = () => {
root.ctx.$toast.close();
}
return {
openToast,
closeToast
}
}
I have other ideas but they seem far fetched an hacky. Keen to hear peoples thoughts on other solutions. I just want a simple way to have 1 instance of a toast, that I can call two functions on to open / close it when and where I want.
This is roughly how I'd do it...
I'd use Composition API, because it makes passing around internals easy
(I'm using popup instead of toast for simplicity)
myPopup.vue
// internal
const popupMessage = Vue.ref('');
const popupVisible = Vue.ref(true);
// external
export const popUpShow = function(message) {
popupMessage.value = message
popupVisible.value = true
}
export const popupHide = function () {
popupVisible.value = false
}
export default {
setup(){
return {
popupMessage, popupVisible, popupHide
}
}
}
Some component, anywhere, composition or class based...
import { popUpShow } from "./myPopup";
export default {
methods: {
myTriggeredEvent() {
popUpShow("I am your Liter")
}
}
}
By exposing popUpShow, which acts as a singleton, you can import that from anywhere, and not have to worry about context.
There the drawback in using this kind of setup/architecture is that it doesn't scale well. The problem happens if your architecture reaches a certain size, and you have multiple triggers coming from various sources that the component needs to have complex logic to handle its state (not likely for this example though). In that case, a managed global store, ie. Vuex, might be a better choice.
I am trying to adapt the design of my app to tablet and one way to detect if the app is running on a tablet is by using the DeviceInfo module in particular the isTablet() method. How can I use this method to conditionally apply styles to an element?
Here is what I am trying to do at the moment:
import { checkIfDeviceIsTablet } from './helper-functions';
<View style={[styles.wrapper, checkIfDeviceIsTablet() === true ? styles.wrapperTablet : {}]}>
{contents}
</View>
The checkIfDeviceIsTablet() function is as follows:
import DeviceInfo from 'react-native-device-info';
function checkIfDeviceIsTablet() {
DeviceInfo.isTablet().then(isTablet => {
return isTablet;
});
}
The issue is that when the component loads the checkIfDeviceIsTablet() method returns a promise as opposed to the expected true/false value and so the conditional styles are not applied when the app is run on a tablet. I tried turning the function into an async/await format with a try/catch but the result is the same.
I would use React Native's own Platform.isPad function but the app must also work on Android.
Any help is appreciated.
I would recommend calling DeviceInfo.isTablet() only once at the beginning of your app. You can store the result globally, and then later on you can check the type without having to deal with async promises.
To store the type globally, your options are:
A global variable
React's Context API
A static property on a class (if using ES6+)
Some sort of global state management solution like Redux
You still have to deal with the initial async problem, since the first call to DeviceInfo.isTablet() will return an async promise.
I'd recommend looking into React's Context API.
Here's a rough example:
render() {
return (
<DeviceInfoContext.Consumer>
{ ({ isTablet }) => (
<Text>Is this a tablet? {isTablet}</Text>
) }
</DeviceInfoContext.Consumer>
)
}
And your DeviceInfoContext class would look something like this:
class DeviceInfoContext extends React.Component {
state = {
isTablet: false
}
componentDidMount() {
Device.IsTablet().then(result => this.setState({ isTablet: result }))
}
render() {
return (
this.props.children({ isTablet: this.state.isTablet })
)
}
}
This is just a rough example. You can learn more about the Context API in the docs
Me too had some troubles with the breaking changes of react native 0.5xx to 0.6xx. The library for device detection change it structure to promises. A paintful.
This library save the day, the installation and use is very easy.
https://github.com/m0ngr31/react-native-device-detection
import { isTablet } from 'react-native-device-detection;
// isTablet is a boolean. Return false o true immediately
//So ...
import styled from 'styled-components/native';
import theme from 'styled-theming';
import { isTablet } from 'react-native-device-detection';
const CoverPageDateText = styled.Text`
font-size: ${isTablet ? 23 : 17};
color: gray;
padding-bottom: 9;
`
I started a new project today using React Native 0.51.0 and noticed that the class syntax for the default project file had something new added, the <{}> syntax after extends Component:
export default class App extends Component<{}> {
...
}
I tried doing research but most search engines ignore special characters even with exact string matching, so trying to find out what this syntax is has proved to be difficult. I did some testing and was able to figure out that this change appeared in v0.49.0. The release notes make no mention of what this added syntax does though.
A lot of vague keyword searching and reading leads me to believe that this may be syntax related to TypeScript, but being unfamiliar with the language, I'm at a loss as to how to search and find out more about the syntax without knowing what the proper term for it is. Could anyone tell me what the name of the syntax and what it does? Specifically with regards to React Native.
It is related to Flow typings for the props you will receive in the component. Component<{}> would mean that you don't expect the component to receive props.
With Flow and React.Component, you can define types for props and state (see React$Component type declaration for details).
Example from Flow documentation about React components
import * as React from 'react';
type Props = { /* ... */ };
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
state = {
count: 0,
};
componentDidMount() {
setInterval(() => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}, 1000);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
<MyComponent />;
If user has selected light-theme, and switches to dark-theme, then all scenes will immediately render to using the dark-theme.
I am using react-native-router-flux if this helps.
Thanks in advance,
Different approaches are possible. One of them is to use React context. Generally it should be used with caution but theming is one of the official examples where it is suitable.
Theming is a good example of when you might want an entire subtree to have access to some piece of information
So the example might look like
class App extends Component {
getChildContext() {
return {theme: { primaryColor: "purple" }};
}
...
}
App.childContextTypes = {
theme: React.PropTypes.object
};
where you set the context for rest of your application and then you use it in your components
class Button extends Component {
render() {
return <TouchableHighlight underlayColor={this.context.theme.primaryColor}>...
}
}
Button.contextTypes = {
theme: React.PropTypes.object
};
If you want to switch theme, you can set context based on your state/props that can be based on user selection.
Currently we are dealing with same questions and therefore we have starting prototyping library called react-native-themeable.
The idea is to use context to store theme and (re)implement RN components so they can replace original components but supports theming.
Example of theme switching you can find in https://github.com/instea/react-native-themeable/blob/master/examples/src/SwitchTheme.js
You can start by creating one JS file containing two objects that have the colors for each theme. Then just use one as a default and if the user picks another theme save it in the database or using local storage and pull the relevant object.
export const theme1 = {
blue: '#fddd',
red: '#ddddd',
buttonColor: '#fff'
}
export const theme2 = {
blue: '#fddd',
red: '#ddddd'
buttonColor: '#fff'
}
Then just import the relevant file:
import * as themes from 'Themes'
const currentTheme = this.props.currentTheme ? this.props.currentTheme : 'theme1'
const styles = StyleSheet.create({
button: {
color: themes[currentTheme].buttonColor
},
})
Wouldn’t it be great if you could import a colors object directly, and modify the object every time you change the theme and have those new colors propagate to the entire app? Then your components wouldn’t need to have any special theme logic. I was able to achieve this using a combination of AsyncStorage, forced app restart, and async module loading.