I have initial state as
sites = [
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
];
I'm trying to add a vehicle to a given site when selected from a list which is in a component SiteVehcleSelection and the method that handles the selection is:
handleVehicleSelection = (event) => {
const vehicle = this.props.vehicles.find((v) => v.id === parseInt(event.target.dataset.id, 10));
this.props.handleVehicleSelection(event, this.state.site.id, {...vehicle});
};
which passes it up to parent SiteList method:
handleVehicleSelection = (event, siteId, vehicle) => {
this.props.dispatch(siteActions.handleVehicleSelect(siteId, vehicle), event.target.checked);
}
called from the SiteList class:
export function handleVehicleSelect(siteId, vehicle, cmd){
return (dispatch) => {
debugger;
return fetch(`${BASE_URL}/accounts/site-vehicle-action/${siteId}/${vehicle.id}/${cmd}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: ''
}).then((res) => {
return res.json();
}).then((json) => {
if (json.msg === true) {
dispatch(vehicleSelect(siteId, vehicle));
}
});
}
}
which dispatches to this:
export function vehicleSelect(siteId, vehicle){
return {type: actionTypes.ADD_VEHICLE_TO_SITE, siteId, vehicle};
}
and my reducer is:
case actionTypes.ADD_VEHICLE_TO_SITE:
debugger;
const siteIndex = state.findIndex((site) => site.id === action.siteId);
console.log(state);
const newState = [...state.slice(0, siteIndex), {...state[siteIndex], vehicles: [...state[siteIndex].vehicles, action.vehicle],}, ...state.slice(siteIndex +1)];
console.log(newState);
return newState;
when I log before and after the changes have taken place, the vehicle has been added to the correct site but it does not show/refresh in the view here is the logging of the state before and after.
Before change :
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(0)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
After change:
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(1)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
can see the first one had the vehicle added correctly and this is the new state but nothing happens on return as if sitesList does not refresh.
Hope this edit helps explain more.
I think below code will shed some light. I assume you have the corresponding indexes.
let state = [
{ id: "id1", name: "name1", items: [{},{}] },
{ id: "id2", name: "name2", items: [{},{}] },
]
function reducer() {
switch("Action") {
case ADD_SITE: { // add new element to state
return [
...state,
payload.site,
]
}
case ADD_SITE_AT_INDEX: { // add new element to state at index: idx
return [
...state.slice(0, idx),
payload.newSite,
...state.slice(idx)
]
}
case ADD_ITEM: { // add new item to site with index: idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items,
payload.newItem
],
},
...state.slice(idx+1)
]
}
case ADD_ITEM_AT_INDEX: { // add new item to site with index: idx, at item index: item_idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items.slice(0, item_idx),
payload.newItem,
...state[idx].items.slice(item_idx),
],
},
...state.slice(idx+1)
]
}
}
}
let say this is your structure of state with keys (i am giving random name to the keys so i can explain)
{
data:[{id:"some_id",name:"some_name",items1:[{},{}]}]
}
//suppose this is your reducer code.
case ADD_ITEM:
return{
...state,
data: state.data.map(val=>{
if(val.Id===action.payload.Id){
return{
...val,
items1:[...val.items1,action.payload.data]
}
}
return{...val}
})
}
here you'll be sending Id and data from the action like:
{type:"ADD_ITEM",payload:{Id,data}}
where id will be the id of first level object which's array needs to be updated,
and data will be the data you want to add into the array..
If you just want to add an object to an array with a given structure, you can do this:
Structure
[
{ id, name, items1[{object},{object}]
]
copy an existing state and then add a new object to the end of the array.
return {
...state,
items1: state.items1.concat(action.newObject),
};
or with ES6 spread
return {
...state,
items1: [...state.items1, action.newObject],
};
// state: { id, name, items1: [{object},{object},{newObject}] }
Related
Currently I am facing a problem that the data from axios POST request to material-table will not render immdiately. I need to refresh it in order to show it. I have a axios request structure like this:
export default function DataWorker() {
const [entries, setEntries] = useState({
data: [
{
id: "",
position: "",
defect: "",
tool: ""
}
]
});
const [state] = React.useState({
columns: [
{ title: "Position", field: "position" },
{ title: "Defect Type", field: "defect" },
{ title: "Tool Decision", field: "tool" }
]
});
const url = "http://127.0.0.1:8000/api/manual_ver_data/"
useEffect(() => {
axios.get(url)
.then(response => {
let data = [];
response.data.forEach(el => {
data.push({
id: el.id,
position: el.position,
defect: el.defect,
tool: el.tool
});
});
setEntries({ data: data });
})
}, []);
return (
<MaterialTable
columns={state.columns}
data={entries.data}
editable={{
onRowAdd: (newData) =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...entries.data];
const populateData = (axiosResponse) => {data.push(axiosResponse)}
axiosCallBack(populateData)
console.log(data)
function axiosCallBack (populateData) {
axios.post(url, newData)
.then(function(res){
populateData(res.data)
})
};
setEntries({ ...entries, data });
}, 600)
})
}}
/>
)
};
This is my view.py:
#api_view(['GET', 'POST'])
def TableViewList(request):
if request.method == 'POST':
serializer = TabelSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
After checking the console.log(data), the data looks like this:
[]
0: {id: 197, position: 'Position 1', defect: 'Defect 1', tool: 'Tool 1'}
length: 1
[[Prototype]]: Array(0)
I found out that after refreshing the page, there is an additional dictionary key tableData.
[]0
0: {id: 197, position: 'Position 1', defect: 'Defect 1', tool: 'Tool 1', tableData: {…}}
length: 1
[[Prototype]]: Array(0)
tableData: {id: 0}
Seems like the material-table require tableData as a key to arrange the row data. But I really want to render the row data without refreshing, is it possible to include the tableData manually? and how to do that?
Imagine you have this data structure:
const data = {
posts: [{
id: 1,
title: "Post 1"
slug: "post-1"
}, {
id: 2,
title: "Post 2"
slug: "post-2"
}],
comments: [{
id: 1,
postId: "post-1",
text: "Comment 1 for Post 1"
}, {
id: 2,
postId: "post-1",
text: "Comment 2 for Post 1"
}, {
id: 3,
postId: "post-2",
text: "Comment 1 for Post 2"
}]
}
An you have the following route /posts/[postId[/[commentId]
so the Next.js structure folder is: posts/[postId]/[commented].js
Then you need to generate the static paths for this routes.
I'm coded the following:
export async function getStaticPaths() {
const { posts, comments } = data
const paths = posts.map((post) => {
return comments
.filter((comment) => comment.postId === post.slug)
.map((comment) => {
return {
params: {
postId: post.slug,
commentId: comment.id
}
}
})
})
}
But it's not working. The throwed error was:
Error: Additional keys were returned from `getStaticPaths` in page "/clases/[courseId]/[lessonId]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { postId: ..., commentId: ... } }
Keys that need to be moved: 0, 1.
How I can "map" or "loop" the data to a proper returned format?
Thanks in advance!
The problem seems to be that your returning this from getStaticPaths data with a wrong shape:
[
[ { params: {} }, { params: {} } ],
[ { params: {} } ]
]
The correct shape is:
[
{ params: {} },
{ params: {} },
{ params: {} }
]
Just tried this and it works.
export async function getStaticPaths() {
const paths = data.comments.map((comment) => {
return {
params: {
postId: comment.postId,
commentId: comment.id
}
}
});
console.log(paths);
return {
paths,
fallback: false
}
};
It generates 3 urls:
/posts/post-1/1
/posts/post-1/2
/posts/post-2/3
Is that what you need?
Like mention #Aaron the problem is for double array of filter y el map.
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
Doc 📚 ➡ https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
I am trying to read json data from server and map. When I enter the same data with hand as a list to the code. It works. But when I am trying to read data from the server it does not work. I know problem is in mapping but I could not figure out. What can be the problem? This is code and json data. Thanks
class App extends Component {
constructor(props) {
super(props);
this.state = {
boards: {
id: null,
name: null,
owner: null,
columns:[]
}
};
}
getBoard() {
fetch("http://localhost:3001/boards/2")
.then(res => res.json())
.then(list => console.log(list))
.then(list => this.setState({boards: list}));
}
componentWillMount() {
this.getBoard();
}
{this.state.boards.columns.map((column, columnIndex) => (<Column
status={this.state.addModalShow}
onModalShow={this.onModalShow}
onHide={addModalClose}
addCard={this.addTask}
column={column}
columnIndex={columnIndex}
key={columnIndex}
onMoveLeft={cardIndex => this.handleMove(columnIndex, cardIndex, DIRECTION_LEFT)}
onMoveRight={cardIndex => this.handleMove(columnIndex, cardIndex, DIRECTION_RIGHT)}
onAddCard={() => this.handleAdd(columnIndex)}
deleteColumn={() => this.deleteColumn(columnIndex)}
addColumn={() => this.handleAdd()}
deleteTask={cardIndex => this.deleteTask(columnIndex, cardIndex)}/>))}
{
id: 2,
name: "Board3",
owner: "Ali",
-columns: [
-{
id: 5,
name: "eee",
cards: [
-{
id: 5,
name: "TestA",
description: "Desc",
link: "google.com",
deadline: "2013-04-06"
},
-{
id: 8,
name: "testB",
description: "Desc",
link: "google.com",
deadline: null
}
]
},
-{
id: 6,
name: "ff",
cards: []
}
]
}
But you don't return anything inside the function that prints the response to the console. Please read about Promise chaining.
Either return list in your second 'then' block or remove it completely.
I'm working on an vue-application where I have a component for driving licenses.
I have the following:
data() {
return {
custom_licenses: [],
basic_licenses: []
}
}
within my methods, I have this:
regular_licenses() {
this.$store.dispatch("license/read").then(response => {
response.licenses.map((license, key) => {
// PUSH LICENSES WITH TYPE 'BASIC' TO this.basic_licenses
// PUSH LICENSES WITH TYPE 'CUSTOM' TO this.custom_licenses
});
});
},
and in my created() i have this:
created() {
this.regular_licenses()
}
The response from my dispatch, returns this:
licenses:
[
{
id: 1,
type: 'basic',
name: 'AMa'
},
{
id: 2,
type: 'basic',
name: 'A2'
},
{
id: 3,
type: 'basic',
name: 'C'
},
{
id: 4,
type: 'custom',
name: 'C1'
},
{
id: 5,
type: 'custom',
name: 'D'
},
and so on...
]
Now I want to loop through the array and separate or push them into custom_licenses and basic_licenses based on the type-attribute - how can I achieve that?
Try this
regular_licenses() {
this.$store.dispatch("license/read").then(response => {
response.licenses.map((license, key) => {
switch (license.type)
case 'basic':
this.basic_licenses.push({ ...license });
break;
case 'custom':
this.custom_licenses.push({ ...license });
break;
});
});
},
Update your Code Block:
response.licenses.map((license, key) => {
// PUSH LICENSES WITH TYPE 'BASIC' TO this.basic_licenses
if(license['type'] == 'basic') {
//deep clone
let tmpLicense = JSON.parse(JSON.stringify(license));
basic_licenses.push(tmpLicense);
} else if(license['type'] == 'custom') {
// PUSH LICENSES WITH TYPE 'CUSTOM' TO this.custom_licenses
//deep clone
let tmpLicense = JSON.parse(JSON.stringify(license));
custom_licenses.push(tmpLicense);
}
});
I have a cats array that I pull from an api
i map over these and render them on a page
each one gets rendered with a like button, when I hit like I want it to like it and when i hit like again, it should unlike it
my initialState is :
state = {
cats: []
};
then once i call the api state looks like this:
cats: [
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
]
I have a like cat method whereby I find the cat that I liked like this:
var cat = this.state.cats.find(c => c.id[0] === cat.id[0])
considering I have all this information, how do I call setState for that specific cat to change the liked from false to true?
I was thinking something like this:
this.setState(prevState => ({ cats: {
cat: {
...prevState.cat,
liked: !prevState.cat.liked
}
}}))
but it does not know what liked is of undefined
any ideas?
One problem with your approach is that there's no prevState.cat.
Assuming the (un)liked cat is stored in cat:
this.setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
Demo:
var state;
function setState(a) {
state = Object.assign(state, a(state));
}
state = {
cats: [
{url: [0], id: [1], source_url: [0], liked: false},
{url: [0], id: [2], source_url: [0], liked: false}
]
};
var cat = state.cats[1];
setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
console.log(state.cats[1].liked);