While I was reading article Persistent Layout Patterns in Next.js, there is a part that I can't get my head around.
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => <div>{/* ... */}</div>
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
At Option 4, static function getLayout is added to AccountSettingsBasicInformation.
However AccountSettingsBasicInformation is a component not an object.
Is it possible to add static function in a component like this way?
Also I created Test component to test it but I got the typescript error saying
Property 'hello' does not exist on type 'FC'.
interface ITestProps {
hello: () => void;
}
const Test: React.FC<ITestProps> = () => {
return <TestFilter />;
};
Test.hello = () => { <div> this is a test </div> } // Test.hello occurs the error
export default Test;
I declared hello type at ITestProps but why I got the error?
anything other than basic types is object, including es6 classes and function.
but typescript has the ability to enforce what properties an object has,
so you can say AccountSettingsBasicInformation.getLayout because AccountSettingsBasicInformation has no type declaration, typescript considers it as any(which includes basic types and object with any properties)
but you declared Test as React.FC<ITestProps> so typescript enforces its properties to be same as React.FC<ITestProps>, so it prints out an error
Related
I have a question. I am currently learning react with Typescript using the - fullstack react with typescript textbook. I am now on the first app - the trello clone.
I hit a bug I am finding difficult to resolve.
I get this error from the AppStateContext.
Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes'
This is the block where it is defined: AppStateContext.tsx
export const AppStateProvider: FC = ({ children }: React.PropsWithChildren<{}>) => {
const [state, dispatch] = useImmerReducer(appStateReducer, appData)
const { lists } = state
const getTasksByListId = (id: string) => {
return lists.find((list) => list.id === id)?.tasks || []
}
return (
<AppStateContext.Provider value={{ lists, getTasksByListId, dispatch }}>
{children}
</AppStateContext.Provider>
)
}
This is the block where it is called: index.tsx
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<AppStateProvider>
<App />
</AppStateProvider>
);
The error is coming from where I call the in the index.tsx file.
I really appreciate the help in advance. React version is 18.2.0
You need to get rid of explicit FC type for AppStateProvider.
See this simplified example:
interface FunctionComponent<P = {}> {
(props: P, context?: any): ReactElement<any, any> | null;
}
As you might have noticed, FunctionComponent is a simplified version (removed props which are not related to your question) of FC.
Generic P represents props argument. If P generic don't have children property, you are not allowed to use it. In fact you are allowed to use only that properties which are declared in P generic.
Please keep in mind, that you have used just FC, it means that P generic is replaced with {}. Hence, you are not allowed to provide any property to react component.
See this:
type Fn<P = 'no properties'> = (props: P) => void
const fn: Fn = (props) => {
props // no properties
}
props has 'no properties' type, and you are not allowed to override this with another type:
// errpr
const fn: Fn = (props:number) => {
props // no properties
}
YOur example is a bit different, because typescript type {} is a tricky type. Since everything in javascript is object, any (not sure for 100%) type is assignable to {}. This is why you don't have an error using React.PropsWithChildren<{}> for props. However, using React.PropsWithChildren<{}> does not affect AppStateProvider type, because when you call it, it will expect only {} as a properties.
Just as a rule of thumb, you are allowed to use either explicit type of whole function and avoid using argument type or just use type for argument.
Either this:
type Fn<P = 'no properties'> = (props: P) => void
const fn: Fn = (props) => {
props // no properties
}
OR
const fn = (props:'no properties') => {
props // no properties
}
P.S. In react 17.*, FC had children type, see this article
The error is basically telling you that the type of children (which is ELements) has no overlap with your typescript defined type (React.PropsWithChildren<{}>). The easy way to fix the issue is to change the AppStateContext.tsx to something like this:
export const AppStateProvider: FC = ({ children }: {children:Elements) => {
...
However, if you really want to practice the clean code, and use React.PropsWithChildren, here is a repository with a sample use case:
https://github.com/KishorNaik/Sol_PropsWithChildren_React
HTH
I have multiple components which all need to do the same thing. (A simple function which maps over their child components and does something to each one). At the moment I am defining this method in each of the components. But I only want to define it once.
I could define it in the top level component and then pass it down as a prop. But that doesn't feel quite right. It is more a library function than a prop. (It seems to me).
What is the correct way of doing this?
Utils.js with latest Javascript ES6 syntax
Create the Utils.js file like this with multiple functions, etc
const someCommonValues = ['common', 'values'];
export const doSomethingWithInput = (theInput) => {
//Do something with the input
return theInput;
};
export const justAnAlert = () => {
alert('hello');
};
Then in your components that you want to use the util functions, import the specific functions that are needed. You don't have to import everything
import {doSomethingWithInput, justAnAlert} from './path/to/Utils.js'
And then use these functions within the component like this:
justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
If you use something like browserify then you can have an external file i.e util.js that exports some utility functions.
var doSomething = function(num) {
return num + 1;
}
exports.doSomething = doSomething;
Then require it as needed
var doSomething = require('./util.js').doSomething;
If you want to manipulate state in helper functions follow this:
Create a Helpers.js file:
export function myFunc(){ return this.state.name; //define it according to your needs }
Import helper function in your component file:
import {myFunc} from 'path-to/Helpers.js'
In your constructor add that helper function to the class
constructor(){ super() this.myFunc = myFunc.bind(this) }
In your render function use it:
`render(){
{this.myFunc()}
}`
Here are some examples on how you can reuse a function (FetchUtil.handleError) in a React component (App).
Solution 1: Using CommonJS module syntax
module.exports = {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
};
Solution 2: Using "createClass" (React v16)
util/FetchUtil.js
const createReactClass = require('create-react-class');
const FetchUtil = createReactClass({
statics: {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
},
render() {
},
});
export default FetchUtil;
Note: If you are using React v15.4 (or below) you need to import createClass as follows:
import React from 'react';
const FetchUtil = React.createClass({});
Source: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass
Component (which reuses FetchUtil)
components/App.jsx
import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {categories: []};
}
componentWillMount() {
window
.fetch('/rest/service/v1/categories')
.then(FetchUtil.handleError)
.then(response => response.json())
.then(categories => this.setState({...this.state, categories}));
}
render() {
return (
<Grid container={true} spacing={16}>
<Grid item={true} xs={12}>
<Categories categories={this.state.categories} />
</Grid>
</Grid>
);
}
}
export default App;
I'll show two styles below, and you'll want to choose depending on how much the components' logic relate to each other.
Style 1 - Relatively related components can be created with callback references, like this, in ./components/App.js...
<SomeItem
ref={(instance) => {this.childA = instance}}
/>
<SomeOtherItem
ref={(instance) => {this.childB = instance}}
/>
And then you can use shared functions between them like this...
this.childA.investigateComponent(this.childB); // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA); // call childB function with childA as arg
Style 2 - Util-type components can be created like this, in ./utils/time.js...
export const getTimeDifference = function (start, end) {
// return difference between start and end
}
And then they can be used like this, in ./components/App.js...
import React from 'react';
import {getTimeDifference} from './utils/time.js';
export default class App extends React.Component {
someFunction() {
console.log(getTimeDifference("19:00:00", "20:00:00"));
}
}
Which to use?
If the logic is relatively-related (they only get used together in the same app), then you should share states between components. But if your logic is distantly-related (i.e., math util, text-formatting util), then you should make and import util class functions.
Another solid option other than creating a util file would be to use a higher order component to create a withComponentMapper() wrapper. This component would take in a component as a parameter and return it back with the componentMapper() function passed down as a prop.
This is considered a good practice in React. You can find out how to do so in detail here.
Sounds like a utility function, in that case why not put it in a separate static utility module?
Otherwise if using a transpiler like Babel you can make use of es7's static methods:
class MyComponent extends React.Component {
static someMethod() { ...
Or else if you are using React.createClass you can use the statics object:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
}
However I don't advise those options, it doesn't make sense to include a component for a utility method.
Also you shouldn't be passing a method down through all your components as a prop it will tightly couple them and make refactoring more painful. I advise a plain old utility module.
The other option is to use a mixin to extend the class, but I don't recommend that as you can't do it in es6+ (and I don't see the benefit in this case).
Shouldn't you use a Mixin for this ? See https://facebook.github.io/react/docs/reusable-components.html
Although they are falling out of favour see https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Might be useful
I have a functional component Foo.js that looks like this:
const Foo = () => {
return (
<View></View>
)
}
export default Foo
This component works fine when rendering it in the app.
The issue is when trying to test the component like this:
import renderer from 'react-test-renderer'
import Foo from './Foo'
test('testing', () => {
const component = renderer.create(<Foo />) <--- Error occurs
})
An error occurs when running this test (when calling renderer.create), saying:
Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
The strange thing is that the test passes without any error if I put the component inside the test file:
const Foo = () => {
return (
<View></View>
)
}
test('testing', () => {
const component = renderer.create(<Foo />)
})
Turns out that the cause for this was that the exported component got mocked, due to that I had automock set to true in my jest config.
Changing this in jest.config.js:
automock: true
Into this:
automock: false
Resolves the issue.
Question: Why is the automocked component not returning anything? Could that be solved in some way so that it would work having automock set to true?
I am attempting to use react-timeout in my application, which I am writing in TypeScript. I am following the "Light Switch" example in the documentation, in which the setTimeout function seems to be defined as a prop upon importing ReactTimeout. I gather this from this line of the example in the documentation:
handleClick = (e) => {
this.props.setTimeout(this.toggle, 5000) // call the `toggle` function after 5000ms
}
However, when I implement it similarly, TypeScript is complaining that the property does not exist. Here (essentially) is my code. The error is noted in the comments:
import * as React from 'react';
import Axios from 'axios';
import ReactTimeout from 'react-timeout';
export interface ExampleProps {
// none at this point
}
export interface ExampleState {
isSearching: boolean;
searchString: string;
}
export default class Example extends React.Component<ExampleProps, ExampleState> {
constructor (props: ExampleProps) {
super(props);
this.state = {
isSearching: false,
searchString: undefined
}
this.onSearchChange = this.onSearchChange.bind(this);
}
onSearchChange(e){
this.setState({searchString: e.target.value});
console.log('searchString: "%s"', this.state.searchString);
// I want to wait 3 seconds before submitting the search, to allow for typing
// ...but I get an error here:
// [ts] Property 'setTimeout' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<ExampleProps>'
this.props.setTimeout(this.doSearch(this.state.searchString), 3000);
//ReactTimeout.setTimeout(this.doSearch(this.state.searchString), 3000);
// calling it directly like this doesn't work either: "Uncaught TypeError: Cannot read property 'setTimeout' of undefined"
}
doSearch(searchTerm) {
this.setState({isSearching: true});
console.log('Now I will go search for "%s"...', searchTerm);
}
render() {
const {
isSearching
} = this.state;
return (
<div>
<h2>Search</h2>
<input type="text" name="searchString" onChange={this.onSearchChange} />
{
isSearching ? <span>Searching...</span> : <span> </span>
}
</div>
)
}
}
The error message is clear to me: setTimeout simply isn't in the props; but I don't know how to define it. If I create my class with React.Component<any, ExampleState>, TypeScript no longer complains, but I get a runtime error, "Uncaught TypeError: this.props.setTimeout is not a function", which isn't surprising, really, given the previous message.
How do I explicitly set a reference to setTimeout from this library? Do I need to do this in my ExampleProps? If so, how?
react-timeout is a so called HOC component, see: https://reactjs.org/docs/higher-order-components.html
So in order for the react-timeout component to pass it's props to your component you have to wrap your component in the react-timeout component, this is usually done through wrapping it in the export, you can see this in the examples on https://github.com/plougsgaard/react-timeout.
Basically try to remove the default export in front of your class example and on the last line of your file do default export ReactTimeout(example)
I'm trying to grasp this piece of code from react-redux-universal-hot-example (my god, look how far we got!).
Anyways..
They are declaring 2 static methods for a class as a reference to 2 function parameters.
export default function connectData(fetchData, fetchDataDeferred) {
return function wrapWithFetchData(WrappedComponent) {
class ConnectData extends Component {
static fetchData = fetchData;
static fetchDataDeferred = fetchDataDeferred;
render() {
return <WrappedComponent {...this.props} />;
}
}
return ConnectData;
};
}
The point is.. This works... But is it supported by either ES6 or ES7? Can you implement a class member as a reference to something you receive as a parameter?
It is not valid ES6 according to the grammar for a ClassElement, and fails when attempted on an ES6 REPL:
const method = () => {};
class Example { static _method = method; }
//=> Unexpected token (2:31)
...but is proposed for ES7+, which presumably is the functionality the babel plugin implements.
Why not? Essentially they return this:
return {
fetchData : fetchData,
fetchDataDeferred : fetchDataDeferred,
render : function() {...},
__proto__ : Component
};
Not exactly but conceptually it is close enough ...