Storing methods needed in all components - javascript

I have a universal app I'm developing for learning purposes. I'm managing the state of my app with Redux, so all my data will be available there. But I want to create some methods that I'm going to use in all my components. The problem is: where should I store this methods?
Adding them to a parent component and passing the methods as props doesn't seem very useful, because this is one of the things that Redux tries to solve. And I'm pretty sure that Redux is not a place for storing methods.
I know I can create a class in a file somewhere, export it, add some methods to it, and when I want to use one method in a component I can call this file, create an instance of the class and call the needed method; but this doesn't look very react to me…
Is there a right way to create methods available for all components?

I've had some success sharing functions between components using an approach similar to the following. I'm not sure this approach will solve your specific use case with regards to cookies, however.
These functions can be stored anywhere and imported wherever required. They accept a component as their first argument, then return a function that operates on the component passed in.
Indicative, untested code follows.
// An event handler than can be shared between multiple components
const handleChange = component => event => component.setState({ value: event.target.value });
class ComponentOne extends PureComponent {
state = {};
render() {
return (
<div>
{this.state.value}
<input onChange={handleChange(this)} />
</div>
);
}
}
class ComponentTwo extends PureComponent {
state = {};
render() {
return (
<div>
{this.state.value}
<input onChange={handleChange(this)} />
</div>
);
}
}

Related

How and where to pass JSON data as a prop when creating components

I am getting some Club information from a JSON I want to use in my React component 'Club'. I created a component ClubList in which all Club components with their corresponding name should be created but I don't know where I should make the HTTP request and where to save it, so I can use it in the return statement.
I tried saving all titles in an array but I stopped at the point I had to pass the titles to each Club element. I just started working with ReactJS so I am a basically complete beginner in ReactJS (Not in JS though).
This is the ClubList class
class ClubList extends React.Component {
render() {
return (
<div className="Clublist">
<Club title="Club1" />
<Club title="Club2" />
...
...
...
</div>
)
}
}
And that is the Club class
class Club extends React.Component {
clubProp = {...}
render() {
return (
<div className="Club">
<div className="image-container">
<img src={this.clubProp.imageSrc} width="300px" height="300px"/>
</div>
<h2>{this.clubProp.name}</h2>
<div className="Business-information">
<div className="Business-address">
<p>{this.clubProp.address}</p>
<p>{this.clubProp.city}</p>
<p>{this.clubProp.zipCode}</p>
</div>
<div className="Business-reviews">
<h3>{this.clubProp.category}</h3>
<h3 className="rating">{this.clubProp.rating}</h3>
<p>90 reviews</p>
</div>
</div>
</div>
)
}
}
I will use an API to get the Club-names but I don't know how I can organize the variables to be accessible in the right places since I don't quite grasp how the scopes in React work. I already have the code for getting the JSON ready, just need to know where to put it and how to pass the values in
Your basic syntax would look like the following. Your component will maintain clubs using component state. In the componentDidMount lifecycle function, you can make your api call and then store the results in your component's state. Any time you call setState, your component will re-render.
class ClubList extends React.Component {
state = {
clubs: []
};
componentDidMount = async () => {
const clubs = await this.fetchClubs();
this.setState({ clubs });
}
render() {
const { clubs } = this.state;
return (
<div className="Clublist">
{clubs.map(club => (
<Club title={club.title} />
)}
</div>
);
}
}
Eventually you can pull out state management from all of your components and use something like redux or MobX and let your components focus solely on rendering html.
Where you should make the API request?
Ideally, we use redux-sagas or redux-thunk as middleware while making API requests. However, since, you're just getting started, you could make the API call in the componentDidMount lifecycle method of your ClubList component.
Now, I am assuming that you receive an array of Clubs. You could map over this array and render each Club component.
Where you should store this data?
Common practice is to use a state-management library like redux with react. It helps scale and maintain your app better. However, you could also use the state of the ClubList component to store the data of your API call.
I hope this was helpful.
From the docs, you make API calls in the componentDidMount life-cycle method. I'd recommend looking at the docs for examples:
https://reactjs.org/docs/faq-ajax.html
The docs use the browser's fetch method to make the request, but I'd personally recommend using axios. https://www.npmjs.com/package/axios, since it's a bit more straight forward.
React uses state, which can be passed down through the component tree. As you are using class components, a typical way of getting your JSON data into the app's state would be to fetch the data in the componentDidMount() lifecyle method of your top level component, and run this.setState({clubProp: result.data}) in the fetch/axios callback. You can pass it to children where they are available as props.
I would argue that Redux is overkill - and that it would be better to defer learning it until you have a state management problem. The new hooks implementation and context API will also change best practices for state management. The guy who created Redux says "Flux libraries are like glasses: you’ll know when you need them."
// Here is the simplified example:
class ClubList extends React.Component {
constructor(props) {
super(props);
this.state = {
ClubDetails: [],
};
}
componentDidMount() {
fetch("api link put here in quotes")
.then(results => {
return results.json();
}).then(data => {
let ClubDetails = data;
this.setState({ ClubDetails: ClubDetails });
})
}
render() {
const ClubDetails = this.state.ClubDetails;
const listItems = ClubDetails.map((clubProp,index) =>
<Club key={index} clubProp={clubProp}/>
);
return (
{listItems}
);
}
}
class Club extends React.Component {
render() {
return (
<div className="Club">
<div className="image-container">
<img src={this.clubProp.imageSrc} width="300px" height="300px"/>
</div>........
My recommendation is using fetch middleware in redux to control all datas in props(You can find in redux github examples), besides the responsive data, you can also monitor fetching status across components.

react how to call state variable from another class?

Search.js
class Search extends Component {
constructor() {
super();
this.state = {
selectedPictures: []
}
}
static getSelectedPictures = () => {
return this.state.selectedPictures;
}
render() {
return (
<div>
...
</div>
);
}
}
export default Search;
Other.js
import Search from './Search';
class Other extends Component {
constructor() {
super();
this.state = {
}
}
render() {
console.log(Search.getSelectedPictures); --> Uncaught null
return (
<div>
...
</div>
);
}
}
export default Other;
How to call Search.state.selectedPictures inside Other.js?
I already try to use static method to return this.state.selectedPictures and call in Other.js, but cannot access.
Any way can import or transfer the var? Both js files are separate files
Thank you.
What you're trying to do isn't really possible in React for a couple of reasons. First of all, you're trying to call methods and access properties on a class, not on an object. You would, in normal (modern) JS, be required to instantiate the class with the new keyword. For example, search = new Search(); search.getSelectedPictures() - this, however, isn't really how React works, and because your classes are actually components, you have to use the <Search/> component syntax in your render function.
As for getting access to the state in Search, you'd need to pass that state from Search to Other.
One way would be to pass the state into the props directly, so in search.js:
render() {
<Other selectedPictures={this.state.selectedPictures} />
}
Then in other.js:
render() {
this.props.selectedPicture.forEach((pic) => <img src={pic} />);
}
Alternatively, you could have a more umbrella parent component, and keep the state in there. Then pass that state to both components simultaneously, if the ones you list are not meant to have a parent-child relationship.
There are also, albeit slightly more complex, ways of doing what you wish but with Search as a child of Other, but without knowing what those two components actually are, it's hard to really tell.
Use flux architecture . The simple implementation is
alt flux
Just create an Action and a Store . When you select images just put them in the Store using Action then get them as props using <AltContainer />

ReactJS - Lifting state up vs keeping a local state

At my company we're migrating the front-end of a web application to ReactJS.
We are working with create-react-app (updated to v16), without Redux.
Now I'm stuck on a page which structure can be simplified by the following image:
The data displayed by the three components (SearchableList, SelectableList and Map) is retrieved with the same backend request in the componentDidMount() method of MainContainer. The result of this request is then stored in the state of MainContainer and has a structure more or less like this:
state.allData = {
left: {
data: [ ... ]
},
right: {
data: [ ... ],
pins: [ ... ]
}
}
LeftContainer receives as prop state.allData.left from MainContainer and passes props.left.data to SearchableList, once again as prop.
RightContainer receives as prop state.allData.right from MainContainer and passes props.right.data to SelectableList and props.right.pins to Map.
SelectableList displays a checkbox to allow actions on its items. Whenever an action occur on an item of SelectableList component it may have side effects on Map pins.
I've decided to store in the state of RightContainer a list that keeps all the ids of items displayed by SelectableList; this list is passed as props to both SelectableList and Map. Then I pass to SelectableList a callback, that whenever a selection is made updates the list of ids inside RightContainer; new props arrive in both SelectableList and Map, and so render() is called in both components.
It works fine and helps to keep everything that may happen to SelectableList and Map inside RightContainer, but I'm asking if this is correct for the lifting-state-up and single-source-of-truth concepts.
As feasible alternative I thought of adding a _selected property to each item in state.right.data in MainContainer and pass the select callback three levels down to SelectableList, handling all the possible actions in MainContainer. But as soon as a selection event occurs this will eventually force the loading of LeftContainer and RightContainer, introducing the need of implementing logics like shouldComponentUpdate() to avoid useless render() especially in LeftContainer.
Which is / could be the best solution to optimise this page from an architectural and performance point of view?
Below you have an extract of my components to help you understand the situation.
MainContainer.js
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
allData: {}
};
}
componentDidMount() {
fetch( ... )
.then((res) => {
this.setState({
allData: res
});
});
}
render() {
return (
<div className="main-container">
<LeftContainer left={state.allData.left} />
<RightContainer right={state.allData.right} />
</div>
);
}
}
export default MainContainer;
RightContainer.js
class RightContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItems: [ ... ]
};
}
onDataSelection(e) {
const itemId = e.target.id;
// ... handle itemId and selectedItems ...
}
render() {
return (
<div className="main-container">
<SelectableList
data={props.right.data}
onDataSelection={e => this.onDataSelection(e)}
selectedItems={this.state.selectedItems}
/>
<Map
pins={props.right.pins}
selectedItems={this.state.selectedItems}
/>
</div>
);
}
}
export default RightContainer;
Thanks in advance!
As React docs state
Often, several components need to reflect the same changing data. We
recommend lifting the shared state up to their closest common
ancestor.
There should be a single “source of truth” for any data that changes
in a React application. Usually, the state is first added to the
component that needs it for rendering. Then, if other components also
need it, you can lift it up to their closest common ancestor. Instead
of trying to sync the state between different components, you should
rely on the top-down data flow.
Lifting state involves writing more “boilerplate” code than two-way
binding approaches, but as a benefit, it takes less work to find and
isolate bugs. Since any state “lives” in some component and that
component alone can change it, the surface area for bugs is greatly
reduced. Additionally, you can implement any custom logic to reject or
transform user input.
So essentially you need to lift those state up the tree that are being used up the Siblings component as well. So you first implementation where you store the selectedItems as a state in the RightContainer is completely justified and a good approach, since the parent doesn't need to know about and this data is being shared by the two child components of RightContainer and those two now have a single source of truth.
As per your question:
As feasible alternative I thought of adding a _selected property to
each item in state.right.data in MainContainer and pass the select
callback three levels down to SelectableList, handling all the
possible actions in MainContainer
I wouldn't agree that this is a better approach than the first one, since you MainContainer doesn't need to know the selectedItems or handler any of the updates. MainContainer isn't doing anything about those states and is just passing it down.
Consider to optimise on performance, you yourself talk about implementing a shouldComponentUpdate, but you can avoid that by creating your components by extending React.PureComponent which essentially implements the shouldComponentUpdate with a shallow comparison of state and props.
According to the docs:
If your React component’s render() function renders the same result
given the same props and state, you can use React.PureComponent for a
performance boost in some cases.
However if multiple deeply nested components are making use of the same data, it makes sense to make use of redux and store that data in the redux-state. In this way it is globally accessible to the entire App and can be shared between components that are not directly related.
For example consider the following case
const App = () => {
<Router>
<Route path="/" component={Home}/>
<Route path="/mypage" component={MyComp}/>
</Router>
}
Now here if both Home and MyComp want to access the same data. You could pass the data as props from App by calling them through render prop. However it would easily be done by connecting both of these components to Redux state using a connect function like
const mapStateToProps = (state) => {
return {
data: state.data
}
}
export connect(mapStateToProps)(Home);
and similarly for MyComp. Also its easy to configure actions for updating relevant informations
Also its particularly easy to configure Redux for your application and you would be able to store data related to the same things in the individual reducers. In this way you would be able to modularise your application data as well
My honest advice on this. From experience is:
Redux is simple. It's easy to understand and scale BUT you should use Redux for some specific use cases.
Since Redux encapsulates your App you can think of storing stuff like:
current app locale
current authenticated user
current token from somewhere
Stuff that you would need on a global scale. react-redux even allows for a #connect decorator on components. So like:
#connect(state => ({
locale: state.locale,
currentUser: state.currentUser
}))
class App extends React.Component
Those are all passed down as props and connect can be used anywhere on the App. Although I recommend just passing down the global props with the spread operator
<Navbar {...this.props} />
All other components (or "pages") inside your app can do their own encapsulated state. For example the Users page can do it's own thing.
class Users extends React.Component {
constructor(props) {
super(props);
this.state = {
loadingUsers: false,
users: [],
};
}
......
You would access locale and currentUser through props because they were passed down from the Container components.
This approach I've done it multiple times and it works.
But, since you wanted to really consolidate the knowledge of React first, before doing Redux you can just store your state on the top-level component and pass it down to the children.
Downsides:
You're gonna have to keep passing them down into inner level components
To update state from the inner level components you're gonna have to pass the function that updates the state.
These downsides are a little boring and cumbersome to manage. That's why Redux was built.
Hope I helped. good luck
By using Redux you can avoid such callbacks and maintain the whole state in one single store - so make your parent component connected component - and make left and right components dumb ones - and just pass in the props you get from parent to child - and you don't have to worry about callbacks in this case.

When should a react component be declared as a function, and not a class?

I'm unsure of when to declare react components as simple standalone functions as opposed to the regular class myComponent extends Component syntax. To use an example from React's docs (located here):
The following is referred to as a "component"
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
While it appears to merely be a function and is declared like any regular old function. Then in the next paragraph, the following is ALSO defined as a component, and looks more like the way I think a component should look:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
What is the difference between these two "components"? Is the first example actually a component if it doesn't inherit from the Component class and isn't being created with React.createClass? I would appreciate somebody explaining this distinction since I couldn't find an answer anywhere in the docs.
When you don't need to use the lifecycle methods and the component isn't stateful you can declare the component as a function. Component lifecycle methods like componentWillMount() and componentDidMount() only can be used if the component is a class that extends Component.
Calculator must be specified as a class-based component because it is dependent upon internal component state i.e. this.setState({...}). Functional components, also known as stateless components do not have a backing instance, thus they are unable to maintain local state in the same way.
Personally, I always try to write functional components as they are arguably easier to test due to their nature of consuming props and returning a tree of ReactElement instances. I will only convert a component to be class-based if it will:
need to manage its own presentation-based state i.e. not applicable to the state of the entire application
benefit from lifecycle methods as a means of improving performance through restricted re-rendering
require references to child ReactElements or DOM nodes via refs
There are two complementary docs from Facebook that explain this quite well:
https://facebook.github.io/react/docs/components-and-props.html
https://facebook.github.io/react/docs/state-and-lifecycle.html
TL;DR a "component" is primarily responsible for representing some DOM. Depending on how your application is organized, your component may or may not need to maintain its own state or have access to the lifecycle methods like componentDidMount or render. In the case that you do need these features, your component should be a class that inherits from React.Component. Otherwise, you can likely get away with writing your component as a plain old function.
If the functional way is more preferred instead of creating classes you could use higher-order components utility called recompose, it has lifecycle HOC.
Small example:
const NewComponent = lifecycle({
componentWillMount() {
this.props.onEnterScreen();
},
})(Component)

ReactJS: adding my own props to another component

Is it possible (or even a good idea) to add my own props to another React component, like:
<SomeCustomComponent myOwnParam={handler}>
As mentioned by Tyrsius, it really depends on the implementation of SomeCustomComponent. If the component does not use the myOwnParam prop anywhere, passing it won't accomplish anything. On the other hand, some React components might use JSX spread attributes to reference props not directly enumerated in the code.
As an example, the following implementation of SomeCustomComponent would pass your myOwnParam prop down to its child div:
class SomeCustomComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
var {customComponentProp, ...other } = this.props;
return (
<div customComponentProp={customComponentProp} {...other}></div>
);
}
}
So again, it depends on the implementation of SomeCustomComponent what will happen.
See Transferring Props documentation for more details: https://facebook.github.io/react/docs/transferring-props.html
This won't cause an error, but unless SomeCustomComponent is looking for this prop nothing will be done with it. It is possible for a component to loop over its props, so this could be a usable strategy, but I am not sure what you would do with it. You couldn't define anything but iteration logic over properties that you don't know in advance.

Categories

Resources