React.createElement: type ไม่ถูกต้อง - ต้องการสตริง


121

พยายามรับ react-router (v4.0.0) และ react-hot-loader (3.0.0-beta.6) เพื่อให้เล่นได้ดี แต่ได้รับข้อผิดพลาดต่อไปนี้ในคอนโซลเบราว์เซอร์:

คำเตือน: React.createElement: type is invalid - คาดว่าจะเป็นสตริง (สำหรับคอมโพเนนต์ในตัว) หรือคลาส / ฟังก์ชัน (สำหรับคอมโพสิตคอมโพสิต) แต่ได้รับ: ไม่ได้กำหนด คุณอาจลืมส่งออกส่วนประกอบของคุณจากไฟล์ที่กำหนดไว้

ดัชนี js:

import React from 'react';
import ReactDom from 'react-dom';
import routes from './routes.js';
require('jquery');
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import './css/main.css';

const renderApp = (appRoutes) => {
    ReactDom.render(appRoutes, document.getElementById('root'));
};

renderApp( routes() );

เส้นทาง js:

import React from 'react';
import { AppContainer } from 'react-hot-loader';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import store from './store/store.js';
import { Provider } from 'react-redux';
import App from './containers/App.jsx';
import Products from './containers/shop/Products.jsx';
import Basket from './containers/shop/Basket.jsx';

const routes = () => (

    <AppContainer>
        <Provider store={store}>
            <Router history={browserHistory}>
                <Route path="/" component={App}>
                    <IndexRoute component={Products} />
                    <Route path="/basket" component={Basket} />
                </Route>
            </Router>
        </Provider>
    </AppContainer>

);

export default routes;

หากคุณใช้react-router-configตรวจสอบให้แน่ใจว่าคุณใช้ไฟล์componentคุณสมบัติแทนrenderเนื่องจากแพคเกจไม่รองรับในภายหลัง ดูข้อมูลเพิ่มเติมเกี่ยวกับ GitHub
totymedli

คำตอบ:


155

ส่วนใหญ่เกิดจากการส่งออก / นำเข้าที่ไม่ถูกต้อง

ข้อผิดพลาดทั่วไป:

// File: LeComponent.js
export class LeComponent extends React.Component { ... }

// File: App.js
import LeComponent from './LeComponent';

ตัวเลือกที่เป็นไปได้:

// File: LeComponent.js 
export default class LeComponent extends React.Component { ... }

// File: App.js
import LeComponent from './LeComponent';

มีหลายวิธีที่อาจผิดพลาด แต่ข้อผิดพลาดนั้นเกิดจากการนำเข้า / ส่งออกไม่ตรงกัน 60% ของเวลาทุกครั้ง

แก้ไข

โดยทั่วไปคุณควรได้รับ stacktrace ที่ระบุตำแหน่งโดยประมาณที่เกิดความล้มเหลว โดยทั่วไปจะตามหลังข้อความที่คุณมีในคำถามเดิม

หากไม่แสดงก็ควรตรวจสอบสาเหตุ (อาจเป็นการตั้งค่าการสร้างที่คุณขาดหายไป) ไม่ว่าจะไม่ปรากฏขึ้นก็ตามวิธีดำเนินการเดียวคือการ จำกัด พื้นที่ที่การส่งออก / นำเข้าล้มเหลว

น่าเศร้าวิธีเดียวที่จะทำได้โดยไม่มี stacktrace คือการลบแต่ละโมดูล / โมดูลย่อยด้วยตนเองจนกว่าคุณจะไม่ได้รับข้อผิดพลาดอีกต่อไปจากนั้นหาวิธีสำรองสแต็ก

แก้ไข 2

จากความคิดเห็นมันเป็นปัญหาการนำเข้าโดยเฉพาะการนำเข้าโมดูลที่ไม่มีอยู่


ได้ดูร่องรอยที่ชี้ไปที่บรรทัด 12 ของเส้นทาง js. บรรทัดนั้นคือ<IndexRoute component={Products} />
JoeTidee

2
คุณแน่ใจหรือไม่ว่าเส้นทางดัชนีเป็นส่วนหนึ่งของ RR4 จริง (ฉันค่อนข้างมั่นใจว่าไม่ใช่)
คริส

นั่นคือปัญหา - ขอบคุณ! ฉันเปลี่ยนกลับไปเป็น v3.0.0 แล้ว
JoeTidee

เยี่ยมมากของดี อย่าลังเลที่จะทำเครื่องหมายเป็นคำตอบที่ได้รับการยอมรับเนื่องจากจะช่วยให้ผู้อ่านในอนาคตทราบวิธีการแก้ไขปัญหาที่คล้ายกัน
คริส

1
@PriyaRanjanSingh มันเป็นไปไม่ได้ที่จะบอกจากความคิดเห็นเพียงเล็กน้อย คุณสามารถโพสต์คำถามใหม่พร้อมรายละเอียดได้หรือไม่?
คริส


11

ลองทำตามนี้

npm i react-router-dom@next

ในApp.jsของคุณ

import { BrowserRouter as Router, Route } from 'react-router-dom'

const Home = () => <h1>Home</h1>

const App = () =>(
  <Router>
    <Route path="/" component={Home} />
  </Router>
)

export default App;

5

คุณจะต้องตระหนักถึงและnamed export default exportดูว่าฉันควรใช้วงเล็บปีกกาสำหรับการนำเข้า ES6 เมื่อใด

ในกรณีของฉันฉันแก้ไขโดยเปลี่ยนจาก

import Provider from 'react-redux'

ถึง

import { Provider } from 'react-redux'

ฉัน จำกัด ปัญหาของฉันให้แคบลงสำหรับผู้ให้บริการรายนี้ อย่างไรก็ตามฉันใช้สัญกรณ์ {Provider} อยู่แล้ว
Jason G

4

ฉันมีปัญหานี้เมื่อฉันเพิ่มไฟล์ css ลงในโฟลเดอร์เดียวกับไฟล์คอมโพเนนต์

คำสั่งนำเข้าของฉันคือ:

import MyComponent from '../MyComponent'

ซึ่งใช้ได้ดีเมื่อมีเพียงไฟล์เดียวคือ MyComponent.jsx (ฉันเห็นรูปแบบนี้ในตัวอย่างและลองดูแล้วก็ลืมไปแล้วว่าทำไปแล้ว)

เมื่อฉันเพิ่ม MyComponent.scss ลงในโฟลเดอร์เดียวกันการนำเข้านั้นล้มเหลว บางที JavaScript อาจโหลดไฟล์. scss แทนดังนั้นจึงไม่มีข้อผิดพลาด

ข้อสรุปของฉัน: ระบุนามสกุลไฟล์เสมอแม้ว่าจะมีเพียงไฟล์เดียวในกรณีที่คุณเพิ่มอีกไฟล์ในภายหลัง


4

อาร์เรย์ของส่วนประกอบ

วิธีทั่วไปในการรับข้อผิดพลาดนี้คือการใช้อาร์เรย์ของส่วนประกอบที่มีดัชนีตำแหน่งที่ใช้เพื่อเลือกส่วนประกอบที่จะแสดงผลจากอาร์เรย์ ฉันเห็นรหัสแบบนี้หลายครั้ง:

const checkoutSteps = [Address, Shipment, Payment]

export const Checkout = ({step}) => {

  const ToRender = checkoutSteps[step]

  return (
    <ToRender />
  )
}

นี้ไม่ได้เป็นรหัสที่ไม่ดีจำเป็น แต่ถ้าคุณเรียกมันว่ามีดัชนีที่ไม่ถูกต้อง (เช่น-1หรือ3ในกรณีนี้) ที่ToRenderองค์ประกอบที่จะundefinedโยนReact.createElement: type is invalid...ข้อผิดพลาด:

<Checkout step={0} /> // <Address />
<Checkout step={1} /> // <Shipment />
<Checkout step={2} /> // <Payment />
<Checkout step={3} /> // undefined
<Checkout step={-1} /> // undefined

วิธีแก้ปัญหาที่มีเหตุผล

คุณควรป้องกันตัวเองและเพื่อนร่วมงานของคุณจากรหัสที่ยากต่อการแก้ไขโดยใช้วิธีการที่ชัดเจนยิ่งขึ้นหลีกเลี่ยงการใช้ตัวเลขวิเศษและใช้ PropTypes:

const checkoutSteps = {
  address: Address,
  shipment Shipment,
  payment: Payment
}

const propTypes = {
  step: PropTypes.oneOf(['address', 'shipment', 'payment']),
}

/* TIP: easier to maintain
const propTypes = {
  step: PropTypes.oneOf(Object.keys(checkoutSteps)),
}
*/

const Checkout = ({step}) => {

  const ToRender = checkoutSteps[step]

  return (
    <ToRender />
  )
}

Checkout.propTypes = propTypes

export default Checkout

และรหัสของคุณจะมีลักษณะดังนี้:

// OK
<Checkout step="address" /> // <Address />
<Checkout step="shipment" /> // <Shipment />
<Checkout step="payment" /> // <Payment />

// Errors
<Checkout step="wrongstep" /> // explicit error "step must be one of..."
<Checkout step={3} /> // explicit error (same as above)
<Checkout step={myWrongVar} /> // explicit error (same as above)

ประโยชน์ของแนวทางนี้

  • รหัสมีความชัดเจนมากขึ้นคุณสามารถเห็นสิ่งที่คุณต้องการแสดงได้อย่างชัดเจน
  • คุณไม่จำเป็นต้องจำตัวเลขและความหมายที่ซ่อนอยู่ ( 1สำหรับที่อยู่2สำหรับ ... )
  • ข้อผิดพลาดก็ชัดเจนเช่นกัน
  • ไม่ต้องปวดหัวกับเพื่อนของคุณ :)

3

สำหรับ Googler ในอนาคต:

วิธีแก้ปัญหาของฉันคือการอัปเกรดreactและreact-domเป็นเวอร์ชันล่าสุดบน NPM เห็นได้ชัดว่าฉันกำลังนำเข้าคอมโพเนนต์ที่ใช้ไวยากรณ์ส่วนใหม่และมันใช้งานไม่ได้ใน React เวอร์ชันเก่าของฉัน


3

โดยส่วนใหญ่จะบ่งชี้ข้อผิดพลาดในการนำเข้า / ส่งออก แต่ระวังอย่าให้แน่ใจว่าไฟล์ที่อ้างถึงในการติดตามสแต็กนั้นถูกเอ็กซ์พอร์ตด้วยตัวมันเองอย่างดี แต่ต้องระวังด้วยว่าไฟล์นี้กำลังอิมพอร์ตคอมโพเนนต์อื่นอย่างถูกต้อง ในกรณีของฉันข้อผิดพลาดเป็นดังนี้:

import React from 'react';

// Note the .css at the end, this is the cause of the error!
import SeeminglyUnimportantComponent from './SeeminglyUnimportantComponent.css';

const component = (props) => (            
  <div>
    <SeeminglyUnimportantComponent />
    {/* ... component code here */}
  </div>    
);

export default component;


ขอบคุณสำหรับความคิดเห็นนี้! ฉันมีปัญหานี้ตั้งแต่อัปเดตบางแพ็คเกจและฉันได้เลื่อนดูความคิดเห็นเหล่านี้ทั้งหมดและไม่มีปัญหาใดเลยในโค้ดนี้ แต่นี่เป็น - ไฟล์ที่มีปัญหาจะนำเข้าสิ่งอื่นที่ทำให้เกิดข้อผิดพลาด
Diane Kaplan

2

ปัญหานี้เกิดขึ้นกับฉันเมื่อฉันมีการอ้างอิงที่ไม่ดีในคำสั่ง render / return (ชี้ไปที่คลาสที่ไม่มีอยู่) ตรวจสอบรหัสใบแจ้งยอดคืนสินค้าของคุณด้วยว่ามีการอ้างอิงที่ไม่ถูกต้องหรือไม่


2

ฉันคิดว่าสิ่งที่สำคัญที่สุดที่ต้องตระหนักเมื่อแก้ไขข้อบกพร่องนี้คือมันจะปรากฏขึ้นเมื่อคุณพยายามสร้างอินสแตนซ์ส่วนประกอบที่ไม่มีอยู่ ไม่จำเป็นต้องนำเข้าส่วนประกอบนี้ ในกรณีของฉันฉันส่งผ่านส่วนประกอบเป็นคุณสมบัติ ฉันลืมอัปเดตหนึ่งในการโทรเพื่อส่งผ่านส่วนประกอบอย่างถูกต้องหลังจากปรับโครงสร้างใหม่ น่าเสียดายเนื่องจาก JS ไม่ได้พิมพ์ข้อผิดพลาดแบบคงที่จึงไม่พบข้อผิดพลาดและต้องใช้เวลาพอสมควรในการค้นหาว่าเกิดอะไรขึ้น

ในการแก้ไขข้อบกพร่องนี้ให้ตรวจสอบส่วนประกอบก่อนที่จะแสดงผลเพื่อให้แน่ใจว่าเป็นประเภทของส่วนประกอบที่คุณคาดหวัง


"มันปรากฏขึ้นเมื่อคุณพยายามสร้างอินสแตนซ์ของส่วนประกอบที่ไม่มีอยู่จริง" - นี่คือสิ่งที่เกิดขึ้นกับฉัน องค์ประกอบที่ถูกนำเข้า / ส่งออกอย่างถูกต้อง แต่ผมผ่านมันเป็นเสาผ่านเส้นทางการอนุมัติในการตอบสนองต่อเราเตอร์และลืมที่จะเปลี่ยนไปrender={props => <Component {...props} />} component={Component}
displacedtexan

2

ฉันพลาดReact Fragment :


function Bar({ children }) {

  return (
    <div>
     {children}
    </div>
  );
}

function Foo() {
  return (
    <Bar>
      <Baz/>
      <Qux/>
    </Bar>
  );
}

โค้ดด้านบนแสดงข้อผิดพลาดด้านบน แต่สิ่งนี้แก้ไขได้:

<Bar>
  <>
    <Baz/>
    <Qux/>
  </>
</Bar>

เกิดขึ้นกับฉันเมื่อฉันเพิ่ม ReactCollapsingTable ( yarn add react-collapsing-table) ฉันจัดการสลับระหว่างคำเตือนนี้ (ซึ่งส่งผลให้เกิดข้อผิดพลาดในการให้น้ำ) และ 'หน้าต่างไม่ได้กำหนด' ที่ตรวจพบภายในโมดูลตารางการยุบตัวที่คอมไพล์แล้ว ... แม้แต่การตัดด้วย<div>...</div>ก็ไม่ช่วยอีกต่อไป
สุดยอด

ในที่สุดฉันก็ถ่ายโอนโครงการทั้งหมดอีกครั้งตั้งแต่เริ่มต้น (แทนการโหลดซ้ำ) และปัญหาก็เกิดขึ้นอีก พบวิธีแก้ปัญหาที่นี่: stackoverflow.com/a/43969990/2821963 (แทนที่จะimport module from 'module-name'ใช้require(module-name).default)
superk

1

สิ่งที่ขาดหายไปสำหรับฉันคือฉันใช้

import { Router, Route, browserHistory, IndexRoute } from 'react-router';

คำตอบแทนหรือคำตอบที่ถูกต้องควรเป็น:

import { BrowserRouter as Router, Route } from 'react-router-dom';

แน่นอนคุณต้องเพิ่มแพ็คเกจ npm react-router-dom :

npm install react-router-dom@next --save

1

ในกรณีของฉันฉันลืมที่จะนำเข้าและส่งออกองค์ประกอบ (ใหม่) ของฉันที่เรียกโดยการแสดงผลในไฟล์ index.js


1

มันหมายความว่าคุณนำเข้า / ส่งออกไม่ถูกต้อง

  • import/exportsตรวจสอบที่เพิ่มใหม่
  • ในกรณีของฉันฉันใช้วงเล็บปีกกาโดยไม่จำเป็น ปัญหาได้รับการแก้ไขโดยอัตโนมัติเมื่อฉันลบวงเล็บปีกกาเหล่านี้ออก
import { OverlayTrigger } from 'react-bootstrap/OverlayTrigger';

0

ในกรณีของฉันลำดับที่คุณสร้างองค์ประกอบและแสดงผลมีความสำคัญ ฉันกำลังเรนเดอร์คอมโพเนนต์ก่อนสร้าง วิธีที่ดีที่สุดคือสร้างคอมโพเนนต์ย่อยจากนั้นคอมโพเนนต์พาเรนต์จากนั้นแสดงผลคอมโพเนนต์หลัก การเปลี่ยนคำสั่งซื้อช่วยแก้ปัญหาให้ฉันได้


0

ในกรณีของฉันฉันต้องอัปเกรดจากreact-router-reduxเป็นreact-router-redux@next. ฉันคิดว่ามันน่าจะเป็นปัญหาความเข้ากันได้


0

พูดง่ายๆก็คือเกิดเหตุการณ์ต่อไปนี้:

render() {
    return (
        <MyComponent /> // MyComponent is undefined.
    );
}

อาจไม่จำเป็นต้องเกี่ยวข้องกับการนำเข้าหรือส่งออกที่ไม่ถูกต้อง:

render() {
    // MyComponent may be undefined here, for example.
    const MyComponent = this.wizards[this.currentStep];

    return (
        <MyComponent />
    );
}

0

หากคุณมีข้อผิดพลาดนี้ขณะทดสอบคอมโพเนนต์ตรวจสอบให้แน่ใจว่าคอมโพเนนต์ย่อยทุกส่วนแสดงผลอย่างถูกต้องเมื่อรันคนเดียวหากองค์ประกอบย่อยของคุณขึ้นอยู่กับรีซอร์สภายนอกในการเรนเดอร์ให้ลองล้อเลียนด้วย jest หรือ libing อื่น ๆ :

ตัวอย่าง:

jest.mock('pathToChildComponent', () => 'mock-child-component')

0

ในกรณีของฉันข้อผิดพลาดเกิดขึ้นเมื่อพยายามใช้ ContextApi ฉันใช้ผิด:

const MyContext = () => createContext()

แต่ควรกำหนดเป็น:

const MyContext = createContext()

ฉันโพสต์ไว้ที่นี่เพื่อให้ผู้เยี่ยมชมในอนาคตที่ติดอยู่กับความผิดพลาดโง่ ๆ เช่นนี้จะได้รับประโยชน์ในการหลีกเลี่ยงการปวดหัวหลายชั่วโมงเพราะสิ่งนี้ไม่ได้เกิดจากการนำเข้า / ส่งออกที่ไม่ถูกต้อง



0

นี่เป็นข้อผิดพลาดที่เป็นวิธีการแก้ไขข้อบกพร่อง ดังที่ได้กล่าวไปแล้วหลายครั้งการนำเข้า / ส่งออกที่ไม่เหมาะสมอาจทำให้เกิดข้อผิดพลาดนี้ได้ แต่น่าแปลกใจที่ฉันได้รับข้อผิดพลาดนี้จากข้อผิดพลาดเล็ก ๆ ในreact-router-dom authentication setupด้านล่างของฉันคือกรณีของฉัน:

การตั้งค่าผิด:

const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route
        {...rest}
        render={(props) => (token ? <Component {...props} /> : <Redirect to={{ pathname: "/login" }} />)}
    />
);

การตั้งค่าที่ถูกต้อง:

const PrivateRoute = ({ component: Component, token, ...rest }) => (
    <Route
        {...rest}
        render={(props) => (token ? <Component {...props} /> : <Redirect to={{ pathname: "/login" }} />)}
    />
);

ความแตกต่างเพียงอย่างเดียวคือฉันกำลังแยกโครงสร้างtokenในไฟล์PrivateRoute component. โดยวิธีการที่โทเค็นได้รับจากlocalstorageสิ่งนี้const token = localStorage.getItem("authUser");ดังนั้นหากไม่มีฉันรู้ว่าผู้ใช้ไม่ได้รับการตรวจสอบสิทธิ์ นอกจากนี้ยังสามารถทำให้เกิดข้อผิดพลาดนั้น


0

มันค่อนข้างง่ายจริงๆ ฉันพบปัญหานี้เมื่อฉันเริ่มเขียนโค้ด React และปัญหามักเกิดขึ้นเพราะการนำเข้า:

import React, { memo } from 'react';

คุณสามารถใช้การทำลายโครงสร้างนี้ได้เนื่องจาก react lib มีคุณสมบัติเป็นบันทึกช่วยจำ แต่คุณไม่สามารถทำลายสิ่งนี้ได้

import { user } from 'assets/images/icons/Profile.svg';

เพราะมันไม่ใช่สิ่งของ

หวังว่าจะช่วยได้!


0

ปฏิกิริยาส่วน

แก้ไขปัญหาให้ฉัน

รหัสข้อผิดพลาด:

 return (
            <section className={classes.itemForm}>
             <Card>
             </Card> 
            </section>
      );

แก้ไข

 return (
      <React.Fragment>
        <section className={classes.itemForm}>
         <Card>
         </Card> 
        </section>
      </React.Fragment>
  );

0

ไม่จำเป็นต้องเป็นปัญหาโดยตรงที่เกี่ยวข้องกับการนำเข้า / ส่งออก ในกรณีของฉันฉันกำลังแสดงผลองค์ประกอบลูกภายในองค์ประกอบหลักและองค์ประกอบลูกมีองค์ประกอบ / แท็ก jsx ซึ่งใช้ แต่ไม่ได้นำเข้า ฉันนำเข้าและใช้แล้วมันก็แก้ไขปัญหาได้ ดังนั้นปัญหาจึงอยู่ในองค์ประกอบ jsx ซึ่งอยู่ภายในองค์ประกอบลูกไม่ใช่การส่งออกองค์ประกอบลูกเอง


0
xxxxx.prototype = {
  dxxxx: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};

คุณต้องเพิ่ม// eslint-disable-line react/forbid-prop-typesแล้วมันได้ผล!


0

ฉันได้รับข้อผิดพลาดเดียวกันทำสิ่งนี้แทน:

npm install react-router@next 
react-router-dom@next
npm install --save history

1
และอย่าลืมรีสตาร์ทการรับชม webpack ของคุณ
Tong Yu

-1

ฉันใช้เวลาเพียง 30 นาทีในการแก้ปัญหาพื้นฐานเบื้องต้นนี้

ปัญหาของฉันคือฉันกำลังนำเข้าองค์ประกอบพื้นเมืองของปฏิกิริยา

เช่น import React, { Text, Image, Component } from 'react';

และพยายามใช้พวกเขาซึ่งทำให้ฉันได้รับข้อผิดพลาดนี้

เมื่อฉันเปลี่ยนจาก<Text>ไป<p>และ<Image>เพื่อให้<img>ทุกอย่างทำงานตามที่คาดไว้


-1

ฉันได้รับข้อผิดพลาดนี้และไม่มีการตอบสนองใด ๆ ที่เป็นกรณีของฉันมันอาจช่วยให้ใครบางคน googling:

ฉันกำหนด Proptype ผิด:

ids: PropTypes.array(PropTypes.string)

มันควรจะเป็น:

ids: PropTypes.arrayOf(PropTypes.string)

VSCode และข้อผิดพลาดในการคอมไพล์ไม่ได้ให้คำแนะนำที่ถูกต้องแก่ฉัน


-2

แก้ไข

คุณกำลังทำให้กระบวนการนี้ซับซ้อน เพียงทำสิ่งนี้:

ดัชนี js:

import React from 'react';
import ReactDom from 'react-dom';
import routes from './routes.js';
require('jquery');
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import './css/main.css';

ReactDom.render(<routes />, document.getElementById('root'));

เส้นทาง js:

import React from 'react';
import { AppContainer } from 'react-hot-loader';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import store from './store/store.js';
import { Provider } from 'react-redux';
import App from './containers/App.jsx';
import Products from './containers/shop/Products.jsx';
import Basket from './containers/shop/Basket.jsx';

const routes =
    <AppContainer>
        <Provider store={store}>
            <Router history={browserHistory}>
                <Route path="/" component={App}>
                    <IndexRoute component={Products} />
                    <Route path="/basket" component={Basket} />
                </Route>
            </Router>
        </Provider>
    </AppContainer>;

export default routes;

แน่ใจหรือว่าปัญหามาจากที่นี่? และไม่ได้มาจากเพจตัวเอง?
John Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.