How to use hooks in custom plugins - docusaurus

I am trying to write a custom plugin for our Docusaurus site. I am able to wire up the custom component, but I cannot use hooks like useState or useEffect. The page crashes saying I'm using an invalid React hook.
I know its possible to use hooks because I see other plugins doing it so I'm sure its a syntax problem somewhere.
Here's my code:
index.ts
import path from 'path'
module.exports = function () {
return {
name: 'docusaurus-theme-myorg-technology',
getThemePath() {
return path.resolve(__dirname, './theme')
}
};
};
theme/index.tsx
import React from 'react'
import {CustomTOC} from './CustomTOC'
const WrappedTOC = (props: any) => {
return (
<CustomTOC {...props} />
);
};
export default WrappedTOC;
theme/CustomTOC.tsx
import React, { useState } from 'react';
import TOC from '#theme-init/TOC';
export default function CustomTOC(props: any) {
//const [tags, setTags] = useState<any[]>([]); <-- if I comment this out the page crashes
return (
<>
<TOC {...props} />
Hello world
</>
);
}

"Invalid hooks call" link to a doc page, that you should read carefully.
Most likely: you are using a different version of React for your component lib that the one Docusaurus uses internally, and it leads to the React lib being used twice at runtime. Make sure the final project will only include one React version. You can for example use the exact same version that the one Docusaurus uses

Related

"Functions are not valid as a React child" error message shows up after adding JavaScript Map function

I added a normal array.prototype.map function to iterate through an array in my code, but keep getting a warning error. I double checked the syntax for the map function and that does not seem to be the issue. I have also tried passing different arrays into the function with different values.
Here is the warning error.
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
And my code:
function App() {
const[items, setItems] =
useState([{toDo: 'vacuum', done: false},
{toDo: 'Do laundry', done: false},
{toDo: 'Walk the dog', done: false}])
return (
<div>
{
(items.map = (item, index) => {
<p>{item}</p>;
})
}
</div>
);
};
This is the error path in the console:
at div
at Home
at Route (http://localhost:3000/static/js/bundle.js:41724:29)
at Switch (http://localhost:3000/static/js/bundle.js:41926:29)
at div
at div
at Router (http://localhost:3000/static/js/bundle.js:41355:30)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:39344:35)
at App
at Router (http://localhost:3000/static/js/bundle.js:41355:30)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:39344:35)
I am wondering if it is at all related to my index.js page.
This is what it currently looks like.
import React from "react";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import reactDom from "react-dom/client";
const root = reactDom.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Any help would be much appreciated!

React-native-web Multi-Platform Setup using Expo 44 + Typescript

What is the simplest way to implement Multi-Platform Setup for a component in Expo. I have tried mamy diferent ways.. it was working on web but it is failing on Native and failing with Jest & #testing-library/react-native. Ideally I would like the least amount of custom config etc (do not want to eject). I expect the file structure to look like this:
Component
|- index.tsx
|- Component.native.tsx
|- Component.web.tsx
I am not sure how to do the index.tsx. I saw someone say something like this would work:
// index.tsx
// #ts-ignore
export { default } from "Component"
this didn't work so I did
// index.tsx
// #ts-ignore
export { default } from "./Component"
This worked for web, but the jest test said
Cannot find './Component'
However, Jest was able to find:
'./Component.mobile.tsx'
'./Component.web.tsx'
I tried:
// index.tsx
// #ts-ignore
import Component from "./Component";
export default Component
and the tests was the same
and the native emulator said:
Unable to resolve module ./Component
I tried using lazy loading but this does not work on web.
import { lazy, Suspense } from "react";
import { Platform } from "react-native";
import Loading from "../../components/Loading";
import { ComponentType } from "./types";
const Web = lazy(() => import("./Component.web"));
const Mobile = lazy(() => import("./Component.mobile"));
const Component: ComponentType = (props) => {
const isWeb = Platform.OS === "web";
return (
<Suspense fallback={<Loading message="Loading Component" />}>
{isWeb ? <Web {...props} /> : <Mobile {...props} />}
</Suspense>
);
};
export default Component
Questions
how to use diferent files for components depending on platform (exlude other files from build)
how to make it ok with ts in vscode
Using Expo 44. Thanks
I would use named exports. So begin by having the same component name in both files. Next I would have one file called Component.tsx and the other Component.native.tsx. That will be enough to allow the bundler to pull the native for native and the other for non-native (in other words web). That should be all you need.

Is is possible to use a custom notification library in react-admin instead of the built-in notifications? [duplicate]

How could i make react-admin to show a multi-line notification / error message on the snackbar?
Having the following dataProvider:
export default (type, resource, params) => {
throw new Error(`
Message line 1.
Message line 2.
Message line 3.
`);
};
That shows a single line message when loading a List component:
error notification screenshot
UPDATE 2022:
Since version 4 of react-admin, you can tell the useNotify hook, to use a multi line message passing the multiLine prop: https://marmelab.com/react-admin/useNotify.html#:~:text=multiLine%3A%20Set%20it%20to%20true%20if%20the%20notification%20message%20should%20be%20shown%20in%20more%20than%20one%20line.
End of UPDATE.
Ok, with the help of the docs I manage to do what I wanted.
Defining a custom Layout component to be used by the App component and passing it a custom Notification component.
// ./MyLayout.js
import React from 'react';
import { Layout } from 'react-admin';
import MyNotification from "../MyNotification";
const CustomLayout = props => (
<Layout {...props} notification={MyNotification} />
);
export default CustomLayout;
Then I pass a custom CSS class to the Notification component.
// ./MyNotification.js
import React from 'react';
import {withStyles} from '#material-ui/core/styles';
import {Notification} from 'react-admin';
// Allow multi-line messages to be displayed
const cssMsg = {
snackbarContent: {
whiteSpace: 'pre-wrap'
}
};
const MyNotification = withStyles(cssMsg)(({classes, ...props}) => (
<Notification {...props} className={classes.snackbarContent}/>
));
export default MyNotification;
error notification screenshot multi-line

Creating and invoking my own javascript library in react-native

I just wanted to encompass some JavaScript functions in a helper class. For example, running some fetch or async operations, etc. I do not want to create a Component class, instead it's just pure JavaScript. I don't think I can just create a js file, plop the code in and invoke it from within a Component. Do I need to register it, etc?
Yes you can through module imports. React native comes packed with babel compiler. You can reference all the enabled Syntax transformer at https://facebook.github.io/react-native/docs/javascript-environment.html.
Babel also has very good explanation on modules at https://babeljs.io/learn-es2015/#ecmascript-2015-features-modules.
For example:
File helper.js
export function doSomething(){
console.log("I am calling module helper through exported function")
}
File App.js
import {doSomething} from "./helper"; //simply imports function from another file.
import React, { Component } from "react";
import { AppRegistry, Text, View} from "react-native";
export default class ExampleComponent extends Component {
componentDidMount(){
doSomething(); //invoke your function here for example.
}
render() {
return (
<View>
<Text>I'm a text</Text>
</View>
)
}
}
AppRegistry.registerComponent("Example", () => ExampleComponent);

How to not put "use strict" everywhere

I'm trying to write some tests for a React app I've been working on, and I figured I would use Jest since it's mentioned in the React docs.
I'm using Webpack and so far I've installed jest-cli, babel-jest, and I included the following configuration in package.json:
"jest": {
"scriptPreprocessor": "./node_modules/babel-jest",
"unmockedModulePathPatterns": [
"./node_modules/react",
"./node_modules/react-dom"
],
}
So, I'm writing the tests for some file foo.js. This file includes some other module bar.js (i.e. const bar = require('./bar');). Unfortunately, when I run jest I get the following error:
SyntaxError: Block-scoped declarations (let, const, function, class) not yet
supported outside strict mode in file 'js/foo.js'.
So, after some research, I find out I have to include 'use strict'; at the top of foo-test.js. However, for some reason, I still get the same error unless I also include 'use strict'; at the top of foo.js.
So my question is: am I doing something wrong? If not, is there anyway for me to write my tests using Jest without having to write 'use strict'; at the top of all my source files?
It seems to test out basic ES2015 classes with jest, use strict is required, however to test React Components, 'use strict' isn't required. Here's an example
//CheckboxWithLabel.js
import React, {Component} from 'react';
class CheckboxWithLabel extends Component {
constructor(){
super(...arguments);
this.state = {
isChecked: false
};
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({
isChecked: !this.state.isChecked
});
}
render() {
return (
<label>
<input type="checkbox"
checked={this.state.isChecked}
onChange={this.onChange} />
{this.state.isChecked ? this.props.labelOn : this.props.labelOff }
</label>
);
}
}
export default CheckboxWithLabel;
//CheckboxWithLabel_tests.js
jest.disableAutomock(); //use this instead of jest.autoMockOff()
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import CheckboxWithLabel from '../source/components/CheckboxWithlabel';
// const CheckboxWithLabel = require('../source/components/CheckboxWithLabel');
describe('CheckboxWithlabel', () => {
const shallowRenderer = TestUtils.createRenderer();
//render a checkbox
const checkbox = TestUtils.renderIntoDocument(
<CheckboxWithLabel labelOn="On" labelOff="Off" />
);
// shallowRenderer.render(<CheckboxWithLabel labelOn="On" labelOff="Off" />)
// const checkbox = shallowRenderer.getRenderOutput();
// it('defaults to unchecked and off label', () => {
// const inputField = checkbox.props.children[0];
// const textNode = checkbox.props.children[1];
// expect(inputField.props.checked).toBe(false);
// expect(textNode).toEqual('Off');
// })
var checkboxNode = ReactDOM.findDOMNode(checkbox);
// let checkboxElement = TestUtils.findRenderedDOMComponentWithTag(checkbox, 'input');
it('defaults to Off label', () => {
expect(checkboxNode.textContent).toEqual('Off');
// expect(checkboxElement.checked).toBe(false);
});
})
Edited: This is not required anymore
Notice the only caveat being that you have to explicitly add a auto_mock_off.js file that simply adds this line (it took me hours to figure this one out)
jest.autoMockOff();
More information can be found on this thread on github Github React Issue #932
That's it! the component testing works perfectly. I've also tried the same example with shallow rendering and it worked perfectly too! Hope this helps!