问题描述
我正在使用Prismic,Gatsby和Typescript建立一个网站。切片功能是Prismic吸引人的一部分,它使您可以创建动态内容部分,以使内容编辑者更易于使用。我有一个名为SliceZone
的组件,它可以映射页面上的所有切片:
SliceZone.tsx
import React from 'react';
import { ImageCta } from 'components/slices/call-to-action/image/ImageCta';
import { Columns } from 'components/slices/columns/Columns';
import { ContentBlock } from 'components/slices/content-block/ContentBlock';
import { Embed } from 'components/slices/embed/Embed';
import { TextHero } from 'components/slices/hero/text/TextHero';
import { Slider } from 'components/slices/slider/Slider';
import { Video } from 'components/slices/video/Video';
interface PageSlicesProps {
slices: any;
}
const PageSlices = ({ slices }: PageSlicesProps) => {
const sliceComponents = {
hero: TextHero,slider: Slider,content_block: ContentBlock,video: Video,columns: Columns,embed: Embed,image_call_to_action: ImageCta,};
return slices.map((slice: any,index: number) => {
const SliceComponent = sliceComponents[slice.type];
if (SliceComponent) {
return <SliceComponent slice={slice} key={`slice-${slice.type}-${index}`} />;
}
});
};
interface SliceZoneProps {
bodyContent: any;
}
export const SliceZone = ({ bodyContent }: SliceZoneProps) => <PageSlices slices={bodyContent.body} />;
,我需要正确键入所有内容。但是,我在SliceComponent
上收到此错误,特别是sliceComponents[slice.type]
:
Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ hero: ({ slice }: any) => Element; slider: ({ slice }: any) => Element; content_block: ({ slice }: any) => Element; video: ({ slice }: any) => Element; columns: ({ slice }: any) => Element; embed: ({ slice }: { ...; }) => Element; image_call_to_action: ({ slice }: any) => Element; }'.
如何创建一个interface
来正确键入此内容?如果我的问题有些混乱,我感到很抱歉,我是React和Typescript的新手,所以到目前为止,这种语言还是有点不适。
解决方法
问题在于您对@page "/Students2";
@using ParentChild.Models;
@inject IJSRuntime JS; // Used for Confirmation Dialog & Alert Box
@inject NavigationManager NavigationManager // Used to Refresh Entire Page
<h3>Students 2</h3>
@if (byStudents == null)
{
<p><em>Loading...</em></p>
}
else if (!byStudents.Any())
{
<p><em>No students in database. Please add some.</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>First</th>
<th>Last</th>
<th>Street</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Cell</th>
<th>Home</th>
<th>Email</th>
<th>Note</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var byStudent in byStudents)
{
<tr>
<td>@byStudent.SFirst</td>
<td>@byStudent.SLast</td>
<td>@byStudent.SStreet</td>
<td>@byStudent.SCity</td>
<td>@byStudent.SState</td>
<td>@byStudent.SZip</td>
<td>@byStudent.SCell</td>
<td>@byStudent.SHome</td>
<td>@byStudent.SEmail</td>
<td>@byStudent.SNote</td>
<td>
<input type="button" class="btn btn-primary" @onclick="@(() => OpenEditStudent(@byStudent.SId))" value="Edit" />
<nbsp></nbsp>
<input type="button" class="btn btn-danger" @onclick="@(async () => await DeleteStudentAction(@byStudent.SId))" value="Delete" />
</td>
</tr>
}
</tbody>
</table>
}
<button class="btn btn-primary" @onclick="() => OpenAddStudent()">Add New Student</button>
@if (showBackdrop)
{
<div class="modal-backdrop fade show"></div>
}
<!-- Define Modal Pop-UP in this Page -->
<div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<!-- Pop-Up Title Section -->
@{
var txt = "";
if ((SId == null) | (SId == ""))
{
txt = "Add Student";
@txt;
}
else
{
txt = "Edit Student";
@txt;
}
}
</h5>
<!-- Pop-Up Close Button at Top -->
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- Pop-Up Body Section -->
<div class="form-group row">
<input id="SId" @bind="SId" type="hidden" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SFirst" class="col-sm-8 col-form-label">First Name:</label>
<input id="SFirst" @bind="SFirst" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SLast" class="col-sm-8 col-form-label">Last Name:</label>
<input id="SLast" @bind="SLast" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SStreet" class="col-sm-8 col-form-label">Street:</label>
<input id="@SStreet" @bind="SStreet" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SCity" class="col-sm-8 col-form-label">City:</label>
<input id="@SCity" @bind="SCity" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SState" class="col-sm-8 col-form-label">State:</label>
<input id="@SState" @bind="SState" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SZip" class="col-sm-8 col-form-label">Zip:</label>
<input id="@SZip" @bind="SZip" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SCell" class="col-sm-8 col-form-label">Cell #:</label>
<input id="@SCell" @bind="SCell" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SHome" class="col-sm-8 col-form-label">Home #:</label>
<input id="@SHome" @bind="SHome" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SEmail" class="col-sm-8 col-form-label">Email:</label>
<input id="@SEmail" @bind="SEmail" class="col-sm-4 form-control" />
</div>
<div class="form-group row">
<label for="@SNote" class="col-sm-8 col-form-label">Note:</label>
<input id="@SNote" @bind="SNote" class="col-sm-4 form-control" />
</div>
</div>
<div class="modal-footer">
<!-- Pop-Up Body Section -->
@{
if ((SId == null) | (SId == ""))
{
<button type="button" class="btn btn-primary" @onclick="async () => await AddStudentAction()">Add</button>
}
else
{
<button type="button" class="btn btn-primary" @onclick="async () => await UpdateStudentAction(this.SId)">Update</button>
}
}
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => this.Close()">Close</button>
</div>
</div>
</div>
</div>
@code {
//-------------------------------------------------------------------------
// Modal Control Variables and Code
//-------------------------------------------------------------------------
private string modalDisplay = "none;";
private string modalClass = "";
private bool showBackdrop = false;
//--- Open Modal...
public void Open()
{
modalDisplay = "block;";
modalClass = "show";
showBackdrop = true;
}
//--- Close Modal...
public void Close()
{
modalDisplay = "none";
modalClass = "";
showBackdrop = false;
}
//-------------------------------------------------------------------------
// Student List from Database
//-------------------------------------------------------------------------
DB_136837_byogaContext db = new DB_136837_byogaContext();
private List<ByStudents> byStudents;
//--- Build Student List on Page Initialization...
protected override void OnInitialized()
{
byStudents = db.ByStudents.ToList();
}
//-------------------------------------------------------------------------
// Modal Parameters
//-------------------------------------------------------------------------
private Modal modal { get; set; }
[Parameter]
public string SId { get; set; } = "";
[Parameter]
public string SFirst { get; set; } = "";
[Parameter]
public string SLast { get; set; } = "";
[Parameter]
public string SStreet { get; set; } = "";
[Parameter]
public string SCity { get; set; } = "";
[Parameter]
public string SState { get; set; } = "";
[Parameter]
public string SZip { get; set; } = "";
[Parameter]
public string SCell { get; set; } = "";
[Parameter]
public string SHome { get; set; } = "";
[Parameter]
public string SEmail { get; set; } = "";
[Parameter]
public string SNote { get; set; } = "";
//-------------------------------------------------------------------------
// Open Modal to Add Student
//-------------------------------------------------------------------------
protected void OpenAddStudent()
{
//-- Clear Any Info Set in Previous Edit Requests...
SId = "";
SFirst = "";
SLast = "";
SStreet = "";
SCity = "";
SState = "";
SZip = "";
SCell = "";
SHome = "";
SEmail = "";
SNote = "";
//--- Open Modal Popup...
this.Open();
}
//-------------------------------------------------------------------------
// Open Modal to Edit Student
//-------------------------------------------------------------------------
protected void OpenEditStudent(int myID)
{
//--- Build & Run SQL Select Statement...
using (var myContext = new DB_136837_byogaContext())
{
//--- Grab Info for Selected Student...
var myQuery = (from s in myContext.ByStudents where s.SId == myID select s).Single();
//--- Set Fields on Popup Form...
this.SId = myQuery.SId.ToString();
this.SFirst = myQuery.SFirst;
this.SLast = myQuery.SLast;
this.SStreet = myQuery.SStreet;
this.SCity = myQuery.SCity;
this.SState = myQuery.SState;
this.SZip = myQuery.SZip;
this.SCell = myQuery.SCell;
this.SHome = myQuery.SHome;
this.SEmail = myQuery.SEmail;
this.SNote = myQuery.SNote;
}
//--- Open Modal Form...
this.Open();
}
//-------------------------------------------------------------------------
// Delete Selected Student from Database with Confirmation Dialog
//-------------------------------------------------------------------------
protected async Task DeleteStudentAction(int myID)
{
bool isConfirmed = await JS.InvokeAsync<bool>("confirm",$"Do you sure you want to permanently delete whis record?");
if (isConfirmed)
{
//--- Build & Run SQL Delete Statement...
using (var myContext = new DB_136837_byogaContext())
{
var myQuery = (from d in myContext.ByStudents where d.SId == myID select d).Single();
myContext.ByStudents.Remove(myQuery);
myContext.SaveChanges();
}
//--- Delete Complete Dialog...
await JS.InvokeVoidAsync("alert","Record Deleted");
//--- Refresh Page...
await InvokeAsync(() =>
{
StateHasChanged();
this.OnInitialized();
});
}
}
//-------------------------------------------------------------------------
// Add Student to Database
//-------------------------------------------------------------------------
protected async Task AddStudentAction()
{
//--- Define the New Student...
var newStudent = new ByStudents();
newStudent.SFirst = SFirst;
newStudent.SLast = SLast;
newStudent.SStreet = SStreet;
newStudent.SCity = SCity;
newStudent.SState = SState;
newStudent.SZip = SZip;
newStudent.SCell = SCell;
newStudent.SHome = SHome;
newStudent.SEmail = SEmail;
newStudent.SNote = SNote;
//--- Add New Student to Database...
using (var myContext = new DB_136837_byogaContext())
{
myContext.ByStudents.Add(newStudent);
myContext.SaveChanges();
}
//--- Close the Popup...
this.Close();
//--- Refresh Page...
await InvokeAsync(() =>
{
StateHasChanged();
this.OnInitialized();
});
}
//-------------------------------------------------------------------------
// Update Student in Database
//-------------------------------------------------------------------------
protected async Task UpdateStudentAction(string myID)
{
//--- Build & Run SQL Select Statement...
using (var myContext = new DB_136837_byogaContext())
{
//--- Update Info for Selected Student...
var myQuery = (from d in myContext.ByStudents where d.SId == Convert.ToInt32(myID) select d).Single();
myQuery.SFirst = this.SFirst;
myQuery.SLast = this.SLast;
myQuery.SStreet = this.SStreet;
myQuery.SCity = this.SCity;
myQuery.SState = this.SState;
myQuery.SZip = this.SZip;
myQuery.SCell = this.SCell;
myQuery.SHome = this.SHome;
myQuery.SEmail = this.SEmail;
myQuery.SNote = this.SNote;
myContext.ByStudents.Update(myQuery);
myContext.SaveChanges();
}
//--- Close the Popup...
this.Close();
//--- Refresh Page...
await InvokeAsync(() =>
{
StateHasChanged();
this.OnInitialized();
});
//--- Refresh Page by Reloading...SHOULD NOT HAVE TO DO THIS!
//NavigationManager.NavigateTo("Refresh/Students2");
}
}
的映射。它缺少签名,因此返回类型隐式为const sliceComponents =
。
如错误所示。
您可以通过说出自己有一个React类组件/功能组件的列表来使之明确吗?
any
const sliceComponentsMap: {
[key: string]: typeof Component | FunctionComponent;
}
是 union 运算符。这说明可以是这些类型中的任何一种。
但是我认为创建一个通用的slice类来扩展React Component类会更好。
|
并让您的slice组件扩展它。
export interface sliceCommonProps {
id: string;
}
// Interface for base slice props
export interface sliceProps<P> {
sliceType: string;
sliceData: P & sliceCommonProps; // This "&" joins the 'Generic' param P with the common props interface
}
export interface sliceState {}
// Base class with two generic params (P,S) for passing specific component props and state interfaces later
export class SliceComponent<
P = { [key: string]: any },S = { [key: string]: any }
> extends Component<sliceProps<P>,sliceState> {}
然后,当您将它们映射到其组件时,可以说它们的类型为SliceComponent。并且编译器现在知道该组件应该具有正确的道具。
export interface ImageCtaProps {
imageUrl: string;
}
export class ImageCta extends SliceComponent<ImageCtaProps> {
render() {
const { id,imageUrl } = this.props.sliceData;
return <div>{imageUrl}</div>;
}
}