I am trying to separate UI and Logic in React class component with little bit of js. I know this could be done easily with custom hooks. But I need to do with class components.
I could make some progress, but it doesnot seem efficient, need your input.
App.view.js
import React from "react";
import Header from "../components/Header";
import ListWrapper from "../components/ListWrapper";
import SearchField from "../components/SearchField";
import UserCard from "../components/UserCard";
import AppController from "./App.controller";
class App extends React.Component {
constructor() {
super();
this.state = { users: [], searchValue: "" };
this.setState = this.setState.bind(this);
}
componentDidMount() {
AppController(this.state, this.setState).fetchUsers();
}
render() {
const filteredUsers = AppController(
this.state,
this.setState
).handleFilter();
return (
<div className="wrapper pb-12 bg-gray-100 mx-auto max-w-7xl">
<Header heading="🐶 Pets Rolodex" />
<SearchField
labelText="Search Pets"
fieldId="pets-search-field"
placeholderText="Enter name"
searchValue={this.state.searchValue}
handleChange={AppController(this.state, this.setState).handleChange}
className="w-72"
/>
<ListWrapper
listData={filteredUsers}
ListItem={UserCard}
ListItemProp="user"
/>
</div>
);
}
}
export default App;
App.controller.js
const AppController = (state, setState) => ({
// Fetch Users info
fetchUsers: () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(data => setState({ users: data }))
.catch(error => console.error(error));
},
// Input Field Handler
handleChange: event => {
const { value } = event.target;
setState({ searchValue: value });
},
// Filter Handler
handleFilter: () => {
const { searchValue, users } = state;
if (searchValue === "") {
return users;
} else {
return users.filter(usr =>
usr.name.toLowerCase().includes(searchValue.toLowerCase())
);
}
}
});
export default AppController;
This works fine. Codesandbox
But the issue is, this instantiates multiple objects from function calls AppController(this.state, this.setState)
and this happens on every render(){...}
It is like I am creating and destroying function stored in memory.
I moved the function call to constructor like below:
constructor() {
super();
this.state = { users: [], searchValue: "" };
this.setState = this.setState.bind(this);
this.controller = AppController(this.state, this.setState)
}
and used this.controller
everywhere in view.
But this seems to persist initial state (empty) into function's execution context and all logic functions inside it never see a light of new state.
Thanks in advance for your input :)
You can refactor the AppController
to class and pass the component instance to its constructor.
E.g.
App.tsx
:
import React from "react";
import ReactDOM from "react-dom";
import Controller from "./controller";
class App extends React.Component<any, { users: any[]; searchValue: string }> {
controller: InstanceType<typeof Controller>;
constructor(props) {
super(props);
this.state = { users: [], searchValue: "" };
this.controller = new Controller(this);
}
componentDidMount() {
this.controller.fetchUsers();
}
render() {
const filteredUsers = this.controller.handleFilter();
return (
<ul>
{filteredUsers.map((user) => (
<li key={user.key}>{user.name}</li>
))}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
controller.ts
:
class Controller {
compInstance;
constructor(compInstance) {
this.compInstance = compInstance;
}
// Fetch Users info
fetchUsers() {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => this.compInstance.setState({ users: data }))
.catch((error) => console.error(error));
}
// Input Field Handler
handleChange(event) {
const { value } = event.target;
this.compInstance.setState({ searchValue: value });
}
// Filter Handler
handleFilter() {
const { searchValue, users } = this.compInstance.state;
if (searchValue === "") {
return users;
} else {
return users.filter((usr) =>
usr.name.toLowerCase().includes(searchValue.toLowerCase())
);
}
}
}
export default Controller;