MobX how to make imported component observable? - reactjs

In my code I am using external library for rendering table its called antd.
I want to re render this imported component when my store state changes but I am unable to make this component observable as its is imported component and it uses data passed as props.
It only worked when I also rendered this store data in main component so it triggered re rendering also this child imported componeent. But don't want to return this data in the main component i want to only return it in the table.
import React from 'react';
import { Table } from 'antd';
#inject('meetingsStore')
#observer
export default class MeetingNotes extends React.Component {
render() {
const meetingsStore = this.props.meetingsStore;
const { agendaPoints } = meetingsStore.getCurrentMeeting();
return (
<Table
rowKey="id"
columns={columns}
dataSource={agendaPoints}
pagination={false}
/>
);
}
}
When I update the store state I want this Table component to re-render.

I managed to fix it by using mobx toJS
Here is an example working code if someone will face a similar issue.
import React from 'react';
import { Table } from 'antd';
import { inject, observer } from 'mobx-react';
import { toJS } from 'mobx';
#inject('meetingsStore')
#observer
export default class MeetingNotes extends React.Component {
render() {
const meetingsStore = this.props.meetingsStore;
const { agendaPoints } = meetingsStore.getCurrentMeeting();
return (
<Table
rowKey="id"
columns={columns}
dataSource={toJS(agendaPoints)}
pagination={false}
/>
);
}
}

I believe that React components work with Mobx, they should be completely rebuilt for this library. Observer components if they are directly accessed by render. Look a similar problem in my question
Ant-Design Table not rendering when change state in mobx store

Related

How to include the Match object into a Reacts component class?

I am using react router v5, and trying to get URL parameters by props match object into my react component class. However it is not working! What am I doing wrong here?
When I create my component as a JavaScript function it all works fine, but when I try to create my component as a JavaScript class it doesn't work.
Perhaps I am doing something wrong? How do I pass the Match object in to my class component and then use that to set my component's state?
here code:
import React, { Component } from 'react';
import { Link} from "react-router-dom";
export class JobDetail extends Component {
state = {
// jobsData: [],
}
async componentDidMount() {
const props = this.props.match;
console.log("---props data---",props); // it's showing undefined
}
render() {
return (
<>
test message
</>
)
}
}
export default JobDetail

React Hook error when loading Solid webId profile

I'm trying to use Solid's react-components to load a user's profile from their webId. I'm running into a problem with useLDflex(). There problem seems to be something to do with React Hooks, but I can't figure it out. My goal is to load the user's profile when the page loads; open to making whatever changes necessary. I'm using MobX for state.
Below is the code and below below is the error in the compiler/web browser. Thank you.
Code (React/JSX/TypeScript):
import React from 'react'; // 16.14.0
import { observer } from 'mobx-react';
import { observable } from 'mobx';
import { useLDflex } from '#solid/react'; // 1.10.0
#observer
export class Profile extends React.Component<{profileId: string}, {}> {
#observable webId = `https://${this.props.profileId}.solidcommunity.net/profile/card#me`;
#observable name = useLDflex(`[${this.webId}`)[0];
render() {
return (
<main role="Profile">
<div className="container">
webId: https://{this.props.profileId}.solidcommunity.net/profile/card#me
Name: {this.name}
</div>
</main>
)
}
}
Error:
src/components/profile/index.tsx
Line 9:24: React Hook "useLDflex" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Search for the keywords to learn more about each error.
You cannot use React Hooks inside class component, ref here: https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
So you need to rewrite it to functional component with Mobx, or make a higher order component and pass the props into your class component (when your class is too complex to rewrite)
With FC:
import {observer} from "mobx-react";
const Profile = observer(({ profileId }) => {
// ...
const name = useLDflex(`...`);
// ...
})
HOC
const withName = (Component) => ({ profileId }) => {
const name = useLDflex('...');
return <Component name={name} profileId={profileId} />
}
export default withName(Profile);

Reactjs redux store changes but in component's props doesn't change

I'm tryin to show navigation depends on changes of categoryURL from
redux store and changing the state in other components. Redux changes the store and it works fine. But in my
component "this.props.categoryUrl" doesn't reflect on value. Can't
find out why?
import React, {Component} from 'react';
import NavigationItems from './NavigationItems/NavigationItems';
import classes from './Navigation.module.css';
import {connect} from 'react-redux';
const mapStateToProps = state => {
console.log(state)
return {
categoryURL: state.categoryUrl
};
};
class navigation extends Component {
componentDidMount() {
console.log(this.props.categoryUrl);
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('NAVIGATION!', this.props.categoryUrl);
}
render() {
let nav = null;
if (this.props.categoryUrl) {
nav = (
<div className={classes.Navigation}>
<NavigationItems/>
</div>
)
}
return (
<>
{nav}
</>
)
}
}
export default connect(mapStateToProps, null)(navigation);
In "normal" React it is needed to use <Navigation/> (Capital letter at the beginning) instead of <navigation/>. Also, If <Navigation/> is being used by another React component then it might be needed to add code that will be executed inside <Navigation/> to refresh the component where you are using <Navigation/> (some kind of callback passed to <Navigation/>). It is this the way or move all the <Navigation/>'s code to the component where you are using <Navigation/>. This is how I solved this kind of problem.

React - How to change code written in the old way when using a new approach?

I was looking for steps on the internet to refactor previous applications for a new approach, but I did not find a satisfactory answer...
I know the previous approach for creating applications in ReactJS which uses the Component and render() function for example, but I can see that it is currently different because:
npx create-react-app app-name
is now generating a different template.
For example, previously it was imported:
import React, {Component} from 'react';
and now only:
import React from 'react';
I am asking for tips or simple advice on what I should change so that the old code works without importing the Component.
Has the method of using functions for communication via AJAX (e.g. loadPerson) changed as well?
For example here is some not working example of ./src/PersonDetail.js:
import React from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export class PersonDetail {
constructor(props) {
super(props);
this.state = {person: null};
}
async componentDidMount() {
const {personId} = this.props.match.params;
const person = await loadPerson(personId);
this.setState({person});
}
render() {
const {person} = this.state;
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
}
}
Thank you in advance.
With the introduction of hooks some core concepts begin to change. Before React 16.8 we used to have a rule of thumb to decide rather a component should be based on class or function:
If the component should hold state then it should be class based. If it doesn't have state (stateless) then it could be a functional component
This used to be true cause there wasn't a way to implement stateful logic inside functional components. Now hooks allow you to implement state in functional components.
The boilerplate generated by create-react-app doesn't import Component from react anymore cause only class based components need to extends from Component and App is now a functional component.
Nothing changed really it's just another way to write your components.
Just import like it used to be:
export class PersonDetail extends React.Component
or give hooks a chance and turn your component into a functional component:
import React, { useState, useEffect } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
const PersonDetail = ({ personID }) => {
const [person, setPerson] = useState(false)
useEffect(() => {
const person = await loadPerson(personId)
setPerson(person)
}, [personID])
return !person ? null : (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
)
}
To add to other comments...
You can have class-based components side by side to functional components, there is no inherent need to re-write any old class-based components you have written.
Introducing Hooks
No Breaking Changes
Before we continue, note that Hooks are:
Completely opt-in. You can try Hooks in a few components without rewriting any existing code. But you don’t have to learn or use Hooks right now if you don’t want to.
100% backwards-compatible. Hooks don’t contain any breaking changes.
Available now. Hooks are now available with the release of v16.8.0.
I am assuming the "new approach" you are talking about are React Hooks.
import * as React from "react";
import { DetailList } from "./DetailList";
import { loadPerson } from "./requests";
const PersonDetail = props => {
const [person, setPerson] = React.useState(null);
React.useEffect(() => {
(async () => {
const { personId } = props.match.params;
setPerson(await loadPerson(personId));
})();
}, []);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
};
export { PersonDetail };
The change you noticed is that create-react-app now creates functional components. This means your components are no longer classes but just functions.
You can still import Component and export a class that extends Component.
You don't need to write your class as a function but to write the example class as a functional component you can do the following:
import React, { useEffect, useState } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export default props => {
const { personId } = props.match.params;
const [person, setPerson] = useState(null);
useEffect(() => {
loadPerson(personId).then(person => setPerson(person));
}, [personId]);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">
Details at {person.name}
</h5>
<DetailList details={person.details} />
</div>
);
};
You can read more about react hooks here
With this import,
import React from 'react';
You can extend Component without importing like,
export class PersonDetail extends React.Component{ ...}
Update
Hooks are newly added in React 16.8 and it is not recommended that we should change our exsiting code with Hooks. We can still have our exsiting class-based approach which extends Component or React.Component.
Hooks gives us the capability of maintaining state of the component as well as it gives a space to write React lifecycle methods.
For example, state in class-based component
state = {
stateVariable : "stateValue"
}
In new approach, it is equivalent to
const [stateVariable, setStateVariable] = useState("stateValue")
And for the lifecycle methods we have useEffect.
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
useEffect(() => {
console.log(stateVariable);
});
of-course we need to import useState and useEffect from react package,
import React, {useState, useEffect} from 'react'
Finally, the class-based component
import React from 'react'
class MyComponent extends React.Component{
state={
stateVariable : "stateValue"
}
componentDidMount(){
console.log(this.state.stateVariable)
}
render(){
return(
<div> {this.state.stateVariable} </div>
)
}
}
can be converted to functional component like,
import React, {useState, useEffect} from 'react'
const MyComponent = (props) => {
const [stateVariable, setStateVariable] = useState("stateValue")
useEffect(()=>{
console.log(stateVariable)
})
return(
<div> {stateVariable} </div>
)
}
Note: We don't have access to this in functional component because we don't have class anymore.
If you want to use class components, you have to make them extend React.Component.
There is another way to create components now, which is the functional way. Meaning that a component can be a simple JavaScript function returning an element (usually, some JSX). In this case, you don't need to import React.Component anymore.
Class component:
import React, { Component } from "react"
class PersonDetail extends Component {
render() {
...
}
...
}
Functional component (this is the way React and the community is now pushing for):
import React from "react"
const PersonDetail = () => {
return (<Your JSX code>)
}

React-Redux: how can i get store data when component first mounts

I've got api data saved in redux store that I use with a few different components. I am trying to figure out a way to access this store data when a new component is first mounted and the data is already in store.
I can't really pass in as a prop because the components are not children. I've considered triggering a MapStateToProps event but that doesn't seem like the correct way. I could call the api again but that doesn't make sense.
Instead of rendering a component you can use a "smart component" and render it:
import React, { PureComponent } from 'react';
import RPT from 'prop-types';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
name: state.user.name
});
class WelcomeMessage extends PureComponent {
static propTypes = {
name: RPT.string.isRequired;
}
render() {
const { name } = this.props;
return (
<h2>
{`Welcome, ${name}`}
</h2>
);
}
}
export default connect(mapStateToProps)(WelcomeMessage);
So in this way your component will always be connected to your store no matter where is located.
Recommended lecture: Difference between smart and dumb components in React.

Resources