Storage-design for fetched data (globally?) & navigation - javascript

im new to JavaScript & React (and new to stackoverflow too) and I hope to get some ideas/ answers regarding the "logic" in implementing data-storage. I have/ had a dev-background in Obj-C / iOS few years back. So some general concepts in web-dev might not be familiar to me.
Hope I'm doing everything correct in my first post, never really used stackoverflow so far :)
So, basically I have a small project with a Sider und Content (using Ant Design Framework). In this project, im trying to figure out, how generell stuff has to be done, like Navigation through the views, data handling and so on.
In one Tab in the SideBar, i'm using a Table (also antd). If you press on one Item, it should show a detail-View with the props of that specific item (later it should be the form for data change/input).
My App.js:
<Content style=...>
<Switch>
<Route exact path="/tasks" >
<TasksTable
data={this.state.tasksData}
handleOnRowClicked = {this.handleOnRowClicked}
/>
</Route>
<Route exact path="/details">
<TasksDetail currentTask= {this.state.currentDetailTask}/>
</Route>
....
</Switch>
</Content>
In the TasksTable-Component:
...
const history = useHistory()
...
function handleOnRowClicked(record, rowIndex,event) {
console.log("Clicked on cell", event)
history.push("/details/")
props.handleOnRowClicked(record.id)
}
return(
<Table
dataSource={props.data}
onRow={ (record, rowIndex) => {
return {
onClick: event => {
handleOnRowClicked(record, rowIndex,event) }
}
}
}
>
<Column title="Name" dataIndex="name" key="name" />
<Column title="Kunde" dataIndex="client_name" key="client_name" />
<Column title="Fertigstellung bis" dataIndex="completion_date" key="completion_date" />
<Column title="HinzugefĆ¼gt" dataIndex="created" key="created" />
<Column title=""
dataIndex="remove_button"
key="remove_button"
render={removeButton => {
return <button key="ButtonKey" onClick={event => handleOnButtonClicked(event)}>Remove</button>
}}/>
</Table>
)
}
What i want to achive:
When clicked on one row of the table, i want to push another "view"/ Side to show details of selected row. Currently I'm doing this in the TasksTable-Component with history.push(). I'm wondering if this is the correct way of showing (detail-)views? Because I need to call props.myHandleCallbackFunc(selectedItem) in App.js to set a State names "currentlySelected" for then passing this state as prop to the pushed detail view.
Hope you get my point.
As far as I understood, I should use ... for automating the navigation-process. Problem is: As I use the Table from antdesign, I need to make this programatically. Thats why I have this workaround (at least it feels like one) with the onRow property... Is this the correct way of using the table? I have the feeling its not.
Problem with my workaround: I've rendered a Button into the Cells to deliver a delete-Button.
Problem there: Even if i click on the Button, it is registered as a click on the row and therefor pushes my detailview... I tried to differentiate with event.target.key but had no success.
Is it the right way of holding all data fetched by my api in the uppermost component and share it to sup-components via props? How can I update it from subcomponents then? Should every Subcomponent should get a handler via props to call back, when value changed?
Or in other words: Make the http-request from the sub-comp or send the changed data to the App-component and let it then trigger the HTTP-POST-request? I strongly guess its second choice.
Thank you so much, I appreciate every answer. Im trying to get things working since few hours now...
(Sorry for spelling mistakes, english is not my native one).
P.S: Actually I wanted to post this yesterday but took few extra hours for working through the "similar questions"... Still, open questions :D
Robin

Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.
https://redux.js.org/introduction/getting-started

Related

Vue 3 Realoading component if variable change

I am trying to realod component if selectedLeague has been changed
<label class="label">Select league</label>
<div class="selector">
<v-select
v-model="selectedLeague"
:dropdown-should-open="dropdownShouldOpen"
:options="leagues"
label="displayLeague"
placeholder="Select a league"
/>
</div>
<div v-if="selectedLeague">
<LeagueTopScorers :is="selectedLeague" :selectedLeague="selectedLeague.id.toString()" />
</div>
In LeagueTopScorers component I am fetching API to get top scorers from selected league.
I tried :watch, v-on:, created()
<LeagueTopScorers /> does re-render when selectedLeague changes 1, but it doesn't re-mount.
It only mounts when selectedLeague changes from a falsy value to a truthy one (because that's when the value of the v-if changes).
There are a few problems with your question:
you're asking about a technical aspect (let's call it X), which you think will solve a business requirement (let's call it Y). It's a classical XY problem. Why don't you describe the Y requirement? (e.g: what you want to achieve). What output change do you want to see on what input change?
the code you posted is not enough to create a runnable example. We don't know what v-select is, what <LeagueTopScorers /> is, or how does :is prop look like on <LeagueTopScorers />.
From the snippet you posted, I am guessing the following:
v-select is either vue-select or Vuetify select component
you expect :is to work on <LeagueTopScorers /> as it does on <component />. Hint: it doesn't, unless you coded it yourself, which I doubt
last, but not least, I guess you want some code you placed into an init lifecycle hook of <LeagueTopScorers /> (e.g: onMounted) to run when you replace an object stored in selectedLeague with another object.
If I'm correct, the simplest and cleanest way to achieve this behavior is to create a computed 2:
const leagueId = computed(() => selectedLeague?.id.toString() || '')
... and use it in v-if, :key and :selectedLeague:
<LeagueTopScorers
v-if="leagueId"
:selectedLeague="leagueId"
:key="leagueId" />
(without the <div v-if> wrapper).
The above will create a new instance of <LeagueTopScorers /> every time leagueId changes and will only render one when leagueId is not falsy. I believe that's what you were trying to accomplish, technically 3.
Notes:
1 - to verify, use onUpdated(() => console.log('updated...'))
2 - use selectedLeague.value?.id.toString() if selectedLeague is a ref
3 - at the same time, I am sure the actual business requirement can be met without creating a new instance of <LeagueTopScorers /> every time leagueId changes, but I can't help more without more details and/or context

Create a custom Show Layout component

I'm trying to create a custom layout component, so I can design my Show page better.
I'm not sure why some things work and some don't. My plan is to use the Material-UI <Grid> component. I know that the Grid component doesn't pass the props, then I'm extending to my own:
export default function OrderShow(props) {
return (
<Show {...props}>
<CustomGrid>
<TextField source="shortId" label="Short ID" />
</CustomGrid>
</Show>
);
}
My CustomGrid component, which is just a draft yet, is cloning its children to pass the props:
function CustomGrid(props) {
return React.Children.map(props.children, (child) => React.cloneElement(child, props));
}
When I use it, the TextField receives the source, and renders it correctly. However the label is gone. I'm not sure why this happens.
Another problem is that when I try to use the CustomGrid with a ReferenceField, it doesn't work at all:
<Show {...props}>
<CustomGrid>
<ReferenceField source="user" reference="users" label="Name" link="show">
<FunctionField render={(record) => `${record.firstName} ${record.lastName}`} />
</ReferenceField>
</CustomGrid>
</Show>
I can see that the props are passed until the ReferenceField, but it's lost before reaching the FunctionField:
Is there a better way to customize the SimpleShowLayout and continue using the TextField and other components from react-admin?
Yes, there is a way. Simply put react-admin's SimpleShowLayout is a little bit more coplex then just passing the props to the children fields. That's why you have to recursively iterate until you reach a field and perform the stuff the react-admin does.
Finally I have decided to share some privately developed components in a new package I am building. I have added layouts which work both with Box and Grid material-ui's components. You can have a look here: ra-compact-ui

Material UI Autocomplete component not showing values from react state

I am trying to get value from the state for materialUI's autocomplete component.
I am facing the following problem : -
Autocomplte working fine for selecting the value and with onChange function it saving it into the state too.
But when I refresh my page/ re-render it is not showing value on the textfeild(from saved state):
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source}
getOptionLabel={option => option.text}
inputValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
I tried with the debugger and found its getting value in this.state.tidelocation_searchtext
but failed to set it with params.
Thanks in advance !!
Ps: I tried with defaultValue and search text nothing worked for me
following is my ONchangeFunction
handleUpdateTideLocationField = (str, value) => {
debugger
this.setState({tidelocation_searchtext: value.text});
}
after selecting a value,following value saved in sate :
tidelocation_searchtext: "Auckland"
So I found the solution all by myself by doing research and several hit and try, following is the solution of my problem:
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source.map(option=>option.text)}
defaultValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
Basically I was doing the following things wrong :
1.There is no need of using input inputValue={this.state.tidelocation_searchtext}& getOptionLabel={option => option.text}
2.as my data is in object form I have to convert it into a string so default value can match this from the state value
options={data_source.map(option=>option.text)}
Thank you all for your valuable support and solution !!
Removing inputValue has worked for me, even when passing object as options.
Using: "#material-ui/core": "^4.12.3"
If data/state not saved externally f.e. in local storage then it will be lost on page refresh, always. It's normal - like RAM memory without power - it (page/app state) only exists in memory !!
It's like using using cookie to keep you logged in.
If you really need such functionality then use f.e. redux-persist
You are right, if object type options are passed, material-ui's AutoComplete component does not seem to reflect the value when mounted. (Perhaps a bug?)
I was able to get around this by passing the proper characters to inputValue.
#RebelCoder
Maybe you should have initialized tidelocation_searchtext.
For me it was coming from the dropdown z-index which was hidden by another css behaviour.
I added this in a css file :
/* Dropdown MUI Component Autocomplete*/
div[role="presentation"].MuiAutocomplete-popper {
z-index: 1000000;
}
And it appeared finally. A bit hacky, but I think it was caused by another library that had something of that kind.
Note that I added several css elements to the selector, because just using the class wasn't enough.

React checkbox state management with react-bootstrap or material-ui?

I am working on a project. I would using checkboxes on a component and would like to show some products which have some features. Features will be defined by user's check. I would like to know how to manage checkbox and state infos together.
I have done this project with <List> and <ListGroup>.I listed a category list in a companent. And when I select one of them(wtih onClick) I can see products which have same CategoryId on my Json-Server.(managed by my_reducer)
I researched on internet and material-ui is more usefull at checkbox management, I think. What do you think? Should I use material-ui instead of react-bootstrap for checkbox?Using both material and bootstrap at the same project will effect my web-page's loading speed? Or does it occurs any other problems?
I'm open with any idea. You can share your idea with me or you can send me guide, docs, examples about it.
ps: Please do not add github or offical pages of checkbox usage(both react and material-ui:) Because I already know them and couldn't find answers for my questions.
Thx.
Since you do not provide any code, it is hard to see what you are trying to achieve and what does "more useful at checkbox management" mean in this context. You mention <List> and <ListGroup>, but how are you using the checkbox, or are you simulating a checkbox with actionable ListGroup items?
I do not think it is a good idea to mix two user interface libraries. You would end up with inconsistent look and feel, and there could be conflicts with styles etc. Adding a new dependency will increase the loading time based on the size of the dependency. If you like material-ui better, it would make sense to migrate the whole project use it.
Here is the code try this may be it works ...and it helps.
class ShowFeatures extends React.Component {
constructor(props) {
super(props);
this.state = {
showfchr: false
};
}
toggleChange = () => {
this.setState({
showfchr: !this.state.showfchr,
});
}
render() {
return (
<React.Fragment>
<div>
<input type="checkbox" checked={this.state.showfchr} onChange={this.toggleChange}
/>
Show Me Some Features!
</div>
{ this.state.showfchr ? (
<div> Your All Feature Will Show here</div>
):(
<div>here is defaullt</div>
)
</React.Fragment>
);
}
}

React and navigating to a selected item

I have a table of user I am displaying to a user. They click a row, and I want to navigate to a component to show the details.
<tbody>
{this.state.clients.map(client => (
<tr className="tableRow" onClick={() => this.handleSelection(client.id)}>
<td>{client.name}</td>
My Route is setup like this:
<Route path="/client/:clientId" component={Client} />
So in the row, I have an onClick event, which calls a function, passing the parameter selected. Here's the function:
handleSelection(clientId) {
this.props.history.push(`/client/${clientId}`);
}
Then, in the constructor of the target component, I am just logging the selected id, which will be passed to the api to load the data:
console.log('Loading clientId...', this.props.match.params.clientId);
But this seem wrong. I get the right ID and all, but .. am I doing this right? Is there a better/safer way to do what I am doing?
Another option would just be to use individual Link tags inside each TD:
<td><Link to={`/client/${client.id}`}>{client.name}</Link></td>
That would also be more accessible, since vision impaired users might have trouble triggering that onclick event.
If you're using react-router-dom it has a Link component
{this.state.clients.map(client => (
<Link to={`/client/${clientId}`} >
<tr className="tableRow">
<td>{client.name}</td>
</Link>
It's better to use a link rather than an onClick on on a table row for accessibility.
If you're not using react-router-dom you can roll your own version from looking at the source - https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/Link.js
By working in this way it'll behave correctly when people command click on the link to open it in a new tab.
For the routes you grab the params as you have, if you don't want the component to deal with match in the properties you can do something like this:
<Route path="/client/:clientId" children={({ match }) => (<Client {...match.params} />)} />

Categories

Resources