An example in the SurviveJS handbook in the chapter on React and Webpack has me confused.
In Note.jsx:
import React from 'react';
export default () => <div>Learn Webpack</div>;
This deviates in a number of ways from what appears to be the standard way of declaring React components using JSX:
import React from 'react';
class Note extends React.Component {
render() {
return <div>Learn Webpack</div>;
}
}
How does the first example work?
How does it know the component is called Note so that it can be referred to as <Note/> in some parent component? Simply by filename match (removing the .jsx part?)
Where's the render() function? And how is it possible to omit it?
What are the limitations of this approach? I am guessing that it only works for wrapping a very simple rendered output, just mapping properties into some HTML...
Where is this style documented? I can't seem to find any official documentation
It doesn't, when you do <Note /> that is just looking for a variable named Note in the local scope. When the component is imported into another file, you can name it whatever you want. e.g. import Note from './Note'; would import the default-exported function in your example.
This is a stateless function component, https://facebook.github.io/react/docs/reusable-components.html#stateless-functions, as you linked to yourself. The function itself is the render, it has no class instance.
They can store no state since they just take inputs and render outputs.
Your specific example is just an arrow function. The documentation linked above uses standard non-arrow functions, but they are essentially interchangable in this case. e.g.
export default () => <div>Learn Webpack</div>;
is the same as
export default function(){
return <div>Learn Webpack</div>;
}
Related
I wrote a component Foo in React.js (its parameter "interface" consists of a single parameter text; there are more parameters in my real code):
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props);
}
render(){
return <div>{this.props.text}</div>;
}
}
and I thought I could improve the code by modifying the property access as:
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props)
this._text = props.text
}
render(){
return <div>{this._text}</div>;
}
}
This would give me the benefits that
I can immediately see what properties are supported by having a look at the constructor.
The code that applies the properties gets shorter/better to read.
However, this destroys the update workflow for the property. In a parent component I use Foo like
<Foo text={this.state.parentText}/>
and
this.setState({parentText: "new text"})
does not trigger an update of the text in Foo any more. The constructor of Foo is only called once and therefore, the private variable this._text is not updated on property changes.
=> Using extra private properties to modify the parameter access turned out to be a bad idea.
=> What would you recommend to have a clear interface for the component without breaking the update workflow?
Some ideas:
a) List all used properties at the start of render (and componentDidUpdate)
render(){
const text = this.props.text;
return <div>{text}</div>;
}
b) Create a getter for each property and put them directly under the constructor, for example
get _text(){
return this.props.text;
}
c) (Only for shorter access.) Try to avoid class components. With function components there is direct access with props.text instead of this.props.text. Or, as a workaround, inherit from a custom component class that passes props argument to render:
render_props(props){
...
}
(Why doesn't react pass this.props as an argument to render by default?)
d) Document the supported properties in a doc string
=> If you know a better option / some standard / best practice, please let me know.
Also tried but failed:
I tried to use state in the child component, with the hope that it would be automatically updated on updates of the parent state:
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props)
this.state = {
text: props.text
}
}
render(){
return <div>{this.state.text}</div>;
}
}
However, this also breaks the update workflow. Using this.state only seems to make sense in the parent component.
Related:
Can I update a component's props in React.js?
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/01.props-in-initial-state.md
Related topic:
How to interact with third party libraries using function components?
https://reactjs.org/docs/integrating-with-other-libraries.html
How do I use/include third party libraries in react?
Integrating React with Other Libraries
Use function components for React >= 16.8, also see recommendation at
https://www.w3schools.com/react/react_class.asp
Use useState hooks instead of setState. This is the modern way to write React, and gives you a simpler way to access state (foo.text, foo.setText). https://reactjs.org/docs/hooks-state.html
Typescript would help with docs (type props = { text: string }), but I also would like the answer for d) (your question is several questions I think).
Use props.text directly, instead of using extra shortcut variable const text = props.text suggested by option a). This way, you don't have a list of all available properties on top of the component function. However, using a consistent props. prefix makes it easier to spot the injected variables in the react code. If there is a huge number of properties and its hard to identify them, try to improve modularization.
JavaScript example code:
Child component Foo:
import React from 'react';
export default function Foo(props){
return <div>{props.text}</div>;
}
}
Parent component:
import React, { useState } from 'react';
import Froo from './foo';
export default function Parent(){
const [parentText, setParentText] = useState('Hello world');
return <Foo text={parentText}/>;
}
I have more than 20 components in my react project. So doing the same thing for every component would be so unwise, so from oop concepts if I create a class with all the functionalities, then I can use it for all the objects I want.
In my case, I want to check if a user is logged in or not. If not redirect to '/login'. Now I have 'NavigationBar' inside each component, where I'm writing some code to redirect or not. But for a few seconds I can see then snap of each components, especially those large ones.
This is not also wise step
So here is what I tried next, created a component named Core extending React.Component, then I extended all component from it, but it gives me error directly.
What is the actual problem here? How this can be solved?
codesandbox.io
just add this line to Child.js
import React from "react";
React doesn't allow creation of component by subclassing defined component, the only way to create component is to extends directly the React.Component class, But react allow you to add extra behavior to defined component by using Higher-Order Components which has purpose to alter defined component by adding wrapper to and existing component
function wrapper(WrappedComponent) {
return class extends React.Component {
render() {
// Here you can add any logic you want
return (<div>
// Here you can add extra code
<h1>Code rendered by wapper</h1>
<WrappedComponent {...this.props} />;
</div>
}
}
}
And you use it like this
import wrapper from './wrapper';
import Child from './Child';
const HigherWrapper = wrapper(Child);
const rootElement = document.getElementById("root");
ReactDOM.render(<HigherWrapper />, rootElement);
I am trying to use the library 'react-images' in my reactjs project. I am receiving the error "Unexpected token" in the react-images\src\components\Footer.js file. Also, on the same line i have the error "'import ... =' can only be used in a .ts file.".
import type { PropsWithStyles, ViewType } from '../types';
Also, I am receiving the error "'type aliases' can only be used in a .ts file." in many of the component files in 'react-images' library. Why is this? Thanks.
import React, { Component, Fragment } from 'react';
import Carousel, { Modal, ModalGateway, FooterCaption } from 'react-images';
You misunderstand how this library is designed. If you look at the documentation, FooterCaption is listed under Component API. That means FooterCaption is part of the API for the Carousel component meaning that this is designed with the Slots Pattern.
If you look at the example code above it, it shows you how this works.
<Carousel components={{ Header: CustomHeader }} />
The Carousel component has a prop called components which takes an object. This object is expected to have keys that are one of the various replaceable components listed under Component API. The value is expected to be a something that React can render; typically a component. This example is replacing the Header with a custom header component called CustomHeader.
If you don't override the component, then Carousel will render something by default as described in the documentation for each component under Component API.
If you would like to override the default FooterCaption, then you will do something like this:
<Carousel components={{ FooterCaption: MyCustomFooterCaption }} />
Where MyCustomFooterCaption is a component you've defined elsewhere. The props it will receive from the Carousel component can be found in the source code.
To summarize:
You don't need to import FooterCaption.
If you plan on overriding the default FooterCaption, then create your own component and pass it in as shown in example.
The same goes for everything listed under Component API.
When i export a component like:
export default class Link extends React.Component{
onLogout() {
this.props.history.push('/');
}
the button that this is tied to correctly changes the page in react-router v4.
However, in my main.js file, I am currently trying to get something like:
if (insert conditional here) {
this.props.history.push('/');
}
to work. But it just give me a type error.
'Uncaught TypeError: Cannot read property 'history' of undefined'.
I do have all the correct dependencies installed, and it works just fine in my other component. I'm currently having this if statement in the main js file (its a small project im practicing to understand v4), so I'm thinking it might be because I'm not extending a class.
Does anyone have any idea why the same code wouldn't be working in the main file, and is there a workaround for this? All the react-router v4 changes are befuddling this rookie.
This means that this.props is not defined, because you are using this.props in a callback where this is not what you think it is. To solve this, use your callback like this:
<button onClick={this.onLogout.bind(this)}>
instead of
<button onClick={this.onLogout}>
You can also do
import { browserHistory } from 'react-router'
... browserHistory.push('/');
Edit: Regarding the latter, you can also wrap your component with this:
import { withRouter } from 'react-router';
// in YourComponent:
... this.props.router.push('/');
export default withRouter(YourComponent);
It may be better than browserHistory because you don't have to specify the history type (could be changed to hashHistory and still work).
I would suggest you to not a address directly into the history but use .
You can send the user to the page and you can conditionally check if he is allowed to see the content if yes then render in render method return the component else return
This would result in a cleaner code.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Redirect.md
I have a redux reducer loaded with several reactjs components.
I want to load these inside other components through this.props
Like: this.props.components.MyReactComponent
class OtherComponent extends Component {
render() {
const Component = this.props.components.MyReactComponent
return (
<div>
<Component />
</div>
)
}
}
Is this possible? If so, how?
EDIT The component is a connected component. I am able to load it but it is broken. In this case, it is a counter, when you click to increment or decrement nothing happens. In the console, there is this error:
Uncaught ReferenceError: _classCallCheck is not defined
if I convert the component into a dumb component (without connecting it), the error is this:
Uncaught ReferenceError: _classCallCheck3 is not defined
EDIT 2
I found out why those errors show up. It is because the react component gets stripped out when stored in the reducer:
A react component would look something like this:
{ function:
{ [Function: Connect]
displayName: 'Connect(Counter)',
WrappedComponent: { [Function: Counter] propTypes: [Object] },
contextTypes: { store: [Object] },
propTypes: { store: [Object] } } }
However, after I store it inside a reducer, it loses its properties and ends up looking something like this:
{ function:
{ [Function: Connect] } }
After reading the comments below, I thought of an alternative. I can store in a reducer the path to each component, then make a new wrapper component that could render those other components from those paths.
I tried it but encoutered a different problem with the funcion require from nodejs that for some weird reason is not letting me user a variable as an argument. For example:
This works:
var SomeContent = require('../extensions/myContent/containers')
This does not:
var testpath = '../extensions/myContent/containers'
var SomeContent = require(testpath)
Giving me the following error:
Uncaught Error: Cannot find module '../extensions/myContent/containers'.
It is adding a period at the end of the path. How can I prevent require to add that period?
If you can think of any other alternative I can implement for what I am trying to do, I would greatly appreciate it.
EDIT 3 Following Thomas advice...
What I am trying to accomplish is this:
I want to be able to render react components inside other react components, I know how to do it the same way most us know how to; however, I want to be able to do it by importing a file that would contain all the components without actually having to import and export each one of them:
OtherComponent.js
import React, { Component } from 'react'
import { SomeComponent } from '../allComponentes/index.js'
export default class OtherComponent extends Component {
render() {
return (
<SomeComponent />
)
}
}
SomeComponent.js
import React, { Component } from 'react'
export default class SomeComponent extends Component {
render() {
return (
<div>
Hello
</div>
)
}
}
allComponents/index.js
import SomeComponent from '../allComponents/SomeComponent/index.js'
export { SomeComponent }
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
allComponents/index.js (pseudocode)
get all folders inside allComponents folder
loop through each folder and require the components
store each component inside an object
export object
When I tried that, I encountered multiple issues, for one, export statements have to be in the top-level, and second, fs would work only on the server side.
So, that is why I thought of loading all the components in a reducer and then pass them as props. But as I found out, they got stripped out when stored them in a reducer.
Then, I thought of only storing the path to those components inside a reducer and have a wrapper component that would use that path to require the needed component. This method almost worked out but the nodejs function require wont allow me to pass a variable as an argument (as shown in EDIT 2)
I think your question is not really to do with redux but rather is (as you say):
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
By way of example, I have all of my (dumb) form components in a folder path components/form-components and the index.js looks something like:
export FieldSet from './FieldSet'
export Input from './Input'
export Label from './Label'
export Submit from './Submit'
export Select from './Select'
export Textarea from './Textarea'
Then when I want to import a component elsewhere, it is import { FieldSet, Label, Input, Submit } from '../../components/form-components/';