I want to organize my code into components and containers folder structure. I will be using Redux with actions and reducers.
What do you think that StartButton is rather component or container? It will not be connected to the redux store, but it has its own state and some decision logic, so maybe it isn't so dumb...
I know my question has something to do with opinions, but perhaps someone can provide me with some insights and what's regarded as best practice.
Here's my StartButton component:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import './style.css';
class StartButton extends Component {
constructor() {
super();
this.state = {
startWasClicked: false,
};
}
handleStartButton = () => {
this.setState({ startWasClicked: true });
};
beerListingView() {
if (this.state.startWasClicked) {
return <div>YES! It was clicked!</div>;
}
// Else return just single <div />
return <div />;
}
render() {
return (
<div>
<div className="StartButton-container">
<RaisedButton
label="Start Here"
className="StartButton-main"
onClick={this.handleStartButton}
/>
</div>
{this.beerListingView()}
</div>
);
}
}
export default StartButton;
In my apps, containers refer to react components that are connected to the Redux store.
components are all the others. Most of them make use of the React state to toggle UI stuff for example. Thats perfectly fine.
Check out https://github.com/react-boilerplate/react-boilerplate for example
Related
When hiddenLogo changes value, the component is re-rendered. I want this component to never re-render, even if its props change. With a class component I could do this by implementing sCU like so:
shouldComponentUpdate() {
return false;
}
But is there a way to do with with React hooks/React memo?
Here's what my component looks like:
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import ConnectedSpringLogo from '../../containers/ConnectedSpringLogo';
import { Wrapper, InnerWrapper } from './styles';
import TitleBar from '../../components/TitleBar';
const propTypes = {
showLogo: PropTypes.func.isRequired,
hideLogo: PropTypes.func.isRequired,
hiddenLogo: PropTypes.bool.isRequired
};
const Splash = ({ showLogo, hideLogo, hiddenLogo }) => {
useEffect(() => {
if (hiddenLogo) {
console.log('Logo has been hidden');
}
else {
showLogo();
setTimeout(() => {
hideLogo();
}, 5000);
}
}, [hiddenLogo]);
return (
<Wrapper>
<TitleBar />
<InnerWrapper>
<ConnectedSpringLogo size="100" />
</InnerWrapper>
</Wrapper>
);
};
Splash.propTypes = propTypes;
export default Splash;
As G.aziz said, React.memo functions similarly to pure component. However, you can also adjust its behavior by passing it a function which defines what counts as equal. Basically, this function is shouldComponentUpdate, except you return true if you want it to not render.
const areEqual = (prevProps, nextProps) => true;
const MyComponent = React.memo(props => {
return /*whatever jsx you like */
}, areEqual);
React.memo is same thing as React.PureComponent
You can use it when you don't want to update a component that you think is static so, Same thing as PureCompoment.
For class Components:
class MyComponents extends React.PureCompoment {}
For function Components:
const Mycomponents = React.memo(props => {
return <div> No updates on this component when rendering </div>;
});
So it's just creating a component with React.memo
To verify that your component doesn't render you can just
activate HightlightUpdates in react extension and check your components reaction on
rendering
We can use memo for prevent render in function components for optimization goal only. According React document:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
According to react documentation:- [https://reactjs.org/docs/react-api.html][1]
React. memo is a higher order component. If your component renders the
same result given the same props, you can wrap it in a call to React.
memo for a performance boost in some cases by memoizing the result.
This means that React will skip rendering the component, and reuse the
last rendered result.
For practical understanding I came across these two videos they are very good if you wanna clear concepts also, better to watch so it'll save your time.
Disclaimer:- This is not my YouTube channel.
https://youtu.be/qySZIzZvZOY [ useMemo hook]
https://youtu.be/7TaBhrnPH78 [class based component]
I'm trying to implement methods to the React import of PESDK (PhotoEditorSDK).
I have an App.js that imports Header, BodyLeft and BodyMiddle without relation between them.
BodyMiddle.js is a template component that renders :
// src/components/BodyMiddle/index.js
import React, { Component } from "react";
import "./BodyMiddle.css";
class BodyMiddle extends Component {
constructor(props){
super(props);
}
handleClick(e) {
e.preventDefault();
// Nothing yet
}
render() {
return (
<div id="BodyMiddle">
<div><button id="resetEditor" onClick={(e) => this.handleClick(e)}>Reset Editor</button></div>
<div class="photo-editor-view"></div>
</div>
);
}
}
export default BodyMiddle;
PhotoEditor.js is the component that calls the PESDK :
// src/components/PhotoEditor/index.js
import React from 'react'
import ReactDOM from 'react-dom'
window.React = React
window.ReactDOM = ReactDOM
import "./PhotoEditor.css";
import "photoeditorsdk/css/PhotoEditorSDK.UI.ReactUI.min.css";
import PhotoEditorUI from 'photoeditorsdk/react-ui'
class PhotoEditor extends React.Component {
constructor(props){
super(props);
}
resetEditor(){
// Empty the image
return this.editor.ui.setImage(new Image());
}
render() {
const { ReactComponent } = PhotoEditorUI;
return (
<div>
<ReactComponent
ref={c => this.editor = c}
license='licence_removed_for_snippet'
assets={{
baseUrl: './node_modules/photoeditorsdk/assets'
}}
editor={{image: this.props.image }}
style={{
width: "100%",
height: 576
}} />
</div>)
}
}
export default PhotoEditor;
Note that the photo-editor-view div class is rendered in BodyLeft.js, by calling the following code and it works well:
ReactDOM.render(<PhotoEditor ref={this.child} image={image} />, container);
Where container is (and I pass an image somewhere else) :
const container = document.querySelector('.photo-editor-view');
What I'm trying to achieve
I would like to keep the reset Button inside BodyMiddle, which is independant and called from App.js, in order to call the PhotoEditor component on the method resetEditor() from anywhere in my app.
That way I could have separated template files that interract with each other.
I've done research and I did not really find an answer yet, I know that React might not be the lib for that, but what are the options ? I see more and more React live apps running with a lot of components interacting, I'm curious.
Thank you for your time !
Best regards
You can use ref on PhotoEditor and save that ref in App, and in the App you can have a method called onResetEditor which calls the ref.resetEditor.
Now you can pass onResetEditor to BodyMiddle or any other component.
Read more about refs in React https://reactjs.org/docs/refs-and-the-dom.html
I have recently encountered an issue regarding the usage of one of my costum components. I have created a "Chargement" (Loading in French) Component for a project I am working on.
This component is a simple circular spinner with a dark background that when displayed, informs the user that an action is going on.
import React, {Fragment} from 'react';
import { CircularProgress } from 'material-ui/Progress';
import blue from 'material-ui/colors/blue';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
const styles = theme => ({
chargement: {
position: 'fixed',
left: '50%',
top: '50%',
zIndex: 1
}
});
class Chargement extends React.Component {
render () {
const { classes } = this.props;
if (this.props.chargement) {
return (
<Fragment>
<div className='loadingicon'>
<CircularProgress size={80} style={{ color: blue[500] }}/>
</div>
<div className='loadingBackground'/>
</Fragment>
);
} else {
return null;
}
}
}
const mapStateToProps = (state) => {
return {
chargement: state.App.chargement
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
}, dispatch);
};
Chargement.propTypes = {
classes: PropTypes.object.isRequired
};
let ChargementWrapped = withStyles(styles)(Chargement);
export default connect(mapStateToProps, mapDispatchToProps)(ChargementWrapped);
This component is displayed based on a boolean variable in my redux Store called "chargement".
It works like a charm whenever I am using it to make api call and load data. However, one of the components in my Web App takes quite a bit of time to render (1-2 seconds). This component renders a pretty big list of data with expansion panels. I tried to set my display variable based on the componentWillMount and componentDidMount functions.
class ListView extends React.Component {
componentWillMount () {
this.props.setChargement(true);
}
componentDidMount () {
this.props.setChargement(false);
}
However with this particular case the "chargement" component never displays.
I also tried to create a "Wrapper Component" in case the issue came from my "chargement" component being somewhat related to the re-rendered component as a children. :
export default class AppWrapper extends React.Component {
render () {
return (
<Fragment>
<Reboot />
<EnTete />
<Chargement />
<App />
</Fragment>
);
}
}
The "App " component is the one that takes a few seconds to render and that I am trying to implement my "chargement" component for. I am pretty sure this as to do with the component lifecycle but everything I tried so far failed.
My current stack is : React with Redux and MaterialUi
What am I missing ?
Thanks for your help!
Ps: You might want to check the explanation and precision I added on the main answer comments as they provide further context.
Not sure if I understood correctly, but I think the problem is simply your API call takes more time than your component mounting cycle, which is totally normal. You can solve the problem by rearranging a bit the places where to put the IO.
Assuming you are making the API call from AppWrapper, dispatch the Redux action in componentDidMount i.e. fetchListItems(). When the API call resolves, the reducer should change its internal loading value from true to false. Then, AppWrapper will receive chargement as a prop and its value will be false. Therefore, you should check what this value is in AppWrapper's render method. If the prop is true, you render the Chargement component or else, render ListView.
Also, try always to decouple the IO from the view. It's quite likely that you'll need to reuse Chargement in other situations, right? Then, make it a simple, generic component by just rendering the view. Otherwise, if you need to reuse the component, it will be coupled to one endpoint already. For this, you can use a Stateless Functional Component as follows:
const Chargement = () =>
<Fragment>
<div className='loadingicon'>
<CircularProgress size={80} style={{ color: blue[500] }}/>
</div>
<div className='loadingBackground'/>
</Fragment>
I found a way to fix my issue that does not involve the use of the "chargement" component like I had initially planned. The issue revolved around the usage of Expansion Panels from the Material-Ui-Next librairy.
The solution I found is the following :
Instead of trying to show a Loading component while my list rendered, I reduced the render time of the list by not rendering the ExpansionDetail Component unless the user clicked to expand it.
This way, the list renders well under 0.2 seconds on any devices I've tested. I set the state to collapsed: false on every panel inside the constructor.
class ListItem extends React.Component {
constructor (props) {
super(props);
this.state = {
collapsed: false
};
this.managePanelState = this.managePanelState.bind(this);
}
managePanelState () {
if (this.state.collapsed) {
this.setState({collapsed: false});
} else {
this.setState({collapsed: true});
}
}
Then I use the onChange event of the expansion panel to switch the state between collapsed and not on every ListItemDetail element.
<ExpansionPanel onChange={() => this.managePanelState()}>
I guess sometimes the solution isn't where you had initially planned.
Thanks to everyone who took time to look into my problem!
I've taken next code from here: https://www.meteor.com/tutorials/react/adding-user-accounts.
Can I replace in this particular case
ReactDOM.findDOMNode(this.refs.container)) with
this.refs.container
without any hidden bugs in future?
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
export default class AccountsUIWrapper extends Component {
componentDidMount() {
// Use Meteor Blaze to render login buttons
this.view = Blaze.render(Template.loginButtons,
ReactDOM.findDOMNode(this.refs.container));
}
componentWillUnmount() {
// Clean up Blaze view
Blaze.remove(this.view);
}
render() {
// Just render a placeholder container that will be filled in
return <span ref="container" />;
}
}
Or maybe even change using callback function:
....
export default class AccountsUIWrapper extends Component {
componentDidMount() {
// Use Meteor Blaze to render login buttons
this.view = Blaze.render(Template.loginButtons,
this.container);
}
....
render() {
// Just render a placeholder container that will be filled in
return <span ref={(node) => (this.container = node) />;
}
}
As suggested in the react refs documentation
If you worked with React before, you might be familiar with an older
API where the ref attribute is a string, like "textInput", and the DOM
node is accessed as this.refs.textInput. We advise against it because
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. If you're currently using
this.refs.textInput to access refs, we recommend the callback pattern
instead.
Hence ref callback is the right way to go if you want to have future support
export default class AccountsUIWrapper extends Component {
componentDidMount() {
this.view = Blaze.render(Template.loginButtons,
this.container);
}
....
render() {
return <span ref={(node) => (this.container = node) />;
}
}
I'm trying to figure out how to user the reducers with and inside my React-Component.
My goal is pretty easy - at least i thought so: I want to toggle a Drawer-Menu. I know I can solve this with React-Only, but I want to learn Redux.
So, I've got a Component…
import React, { Component } from 'react';
class Example extends Component {
// ???
render() {
return (
<button className="burgerbutton" onClick={this.toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
);
}
}
export default Example;
also a Reducer
const initialState = {
buttonstate: false
};
const example = (state = initialState, action) => {
switch (action.type) {
case 'TOGGLE_BTN':
return Object.assign({}, state, {
buttonstate: !state.buttonstate
})
default:
return state
}
}
export default example
and an Action (although I don't know where to put that since it's so simple)
export const toggleDrawer = () => {
return {
type: 'TOGGLE_DRAWER'
}
}
I read a lot of tutorials and most of them want me to seperate between "Presentational Components" and "Container Components". I can't really see how these concepts apply here.
So what do I have to do to do to make this work? Am I looking at this problem from the right angle or do I need 12 "Container Components" to solve this?
I really hope this question makes sense at all and/or is not a duplicate!
In redux you have to dispatch action to update reducer state. So normally a component is connected to the redux store and communication is done through dispatch.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { toggleDrawer } from 'action file location';
class Example extends Component {
toggleDrawerHandler() {
this.props.dispatch(toggleDrawer())
}
render() {
// access button state from this.props.buttonstate
return (
<button className="burgerbutton" onClick={this.toggleDrawerHandler.bind(this)}</button>
<div className="drawerMenu isvisible" ></div>
);
}
}
export default connect((store) => {buttonstate: store.buttonstate})(Example);
First, I'm really enjoying using redux "ducks" which is basically a redux reducer bundle. You put your reducer, action constants, and action creators in one file (called a duck). Then you may have multiple ducks for different modules or pieces of state that you'd then combine with combineReducers.
While #duwalanise has the right idea, I'd rather see the second param of connect() be used to directly map the action to dispatch (and there's a good shortcut for it) instead of having to use this.props.dispatch
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { toggleDrawer } from './duck';
class Example extends Component {
render() {
const { buttonstate, togglerDrawer } = this.props;
return (
<div>
<button className="burgerbutton" onClick={toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
</div>
);
}
}
const mapStateToProps = (state) => ({
buttonstate: state.buttonstate,
});
export default connect(mapStateToProps, { toggleDrawer })(Example);
One side note, if you have a handler method in your component, it's better to do .bind(this) inside the constructor instead of using an arrow function or .bind(this) inside the event, ie don't do this onClick={() => /* do something */ } or this onClick={this.myHandler.bind(this)} This is an interesting (and long) read on it.
To touch on the Container vs Presentational Component piece: The idea would be to put all of your logic, handlers, redux actions etc into your containers, and pass that through props to your simple (hopefully stateless/pure function) presentational components. Technically, your component the way it's written could be turned into a stateless component:
const Example = ({ buttonstate, togglerDrawer }) => (
<div>
<button className="burgerbutton" onClick={toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
</div>
);