验证码输入 框

import React from 'react';
import './index.css';
export default class InputGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
boxLength: [],
numList: [],
isFocus: false,
num: ''
};
this.textChange = this.textChange.bind(this);
this.inputFocus = this.inputFocus.bind(this);
}
textChange(e) {
const value = e.target.value.replace(/[^\d]/g, '')
this.props.getValue(value)
this.setState({
num: value
})
setTimeout(() => {
this.setState({
numList: this.state.num.split('')
})
}, 100);
}
inputFocus() {
this.refs.focus()
this.setState({ isFocus: true })
}
componentDidMount() {
const { length } = this.props;
switch (length) {
case 4:
this.setState({
boxLength: [0, 1, 2, 3]
})
break;
case 6:
this.setState({
boxLength: [0, 1, 2, 3, 4, 5]
})
break;
default:
this.setState({
boxLength: [0, 1, 2, 3, 4, 5]
})
}
}
render() {
const { length, type } = this.props;
const { num, numList, isFocus, boxLength } = this.state;
return (

{
type === 'line' ?

this.refs=ref}
className="box-input"
maxLength={length}
value={num}
onChange={this.textChange}
onBlur={() => this.setState({ isFocus: false })}
/>

{boxLength.map((item, index) => {
return (

{numList[item]}|

)
})}

: ''
}
{
type === 'box' ?

this.refs=ref}
className="box-input"
maxLength={length}
value={num}
onChange={this.textChange}
onBlur={() => this.setState({ isFocus: false })}
/>

{boxLength.map((item, index) => {
return (

{numList[item]}|

)
})}

: ''
}

);
}
}

Redux-Form

Field Array example
https://www.jianshu.com/p/4b5cb1c32f40
[cc lang=”js”]
[/cc]
renderField 是一个组件 接收有以下props: meta: { touched, error, submitFailed } 信息, input, label, type 等
renderMembers 是一个 组件, 有以下props: fields ,meta: { touched, error, submitFailed } 信息, input, label, type 等
renderMembers 里 fields 是个数组, field.push 是添加新成员

[cc lang=”js”]
const renderMembers = ({ fields, meta: { touched, error, submitFailed } }) => (


  • {(touched || submitFailed) && error && {error}}
  • {fields.map((member, index) => (

  • ))}

);
const FieldArraysForm = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (



);
};

const validate = values => {
const errors = {};
if (!values.clubName) {
errors.clubName = ‘Required’;
}
if (!values.members || !values.members.length) {
errors.members = { _error: ‘At least one member must be entered’ };
} else {
const membersArrayErrors = [];
values.members.forEach((member, memberIndex) => {
const memberErrors = {};
if (!member || !member.firstName) {
memberErrors.firstName = ‘Required’;
membersArrayErrors[memberIndex] = memberErrors;
}
if (!member || !member.lastName) {
memberErrors.lastName = ‘Required’;
membersArrayErrors[memberIndex] = memberErrors;
}
if (member && member.hobbies && member.hobbies.length) {
const hobbyArrayErrors = [];
member.hobbies.forEach((hobby, hobbyIndex) => {
if (!hobby || !hobby.length) {
hobbyArrayErrors[hobbyIndex] = ‘Required’;
}
});
if (hobbyArrayErrors.length) {
memberErrors.hobbies = hobbyArrayErrors;
membersArrayErrors[memberIndex] = memberErrors;
}
if (member.hobbies.length > 5) {
if (!memberErrors.hobbies) {
memberErrors.hobbies = [];
}
memberErrors.hobbies._error = ‘No more than five hobbies allowed’;
membersArrayErrors[memberIndex] = memberErrors;
}
}
});
if (membersArrayErrors.length) {
errors.members = membersArrayErrors;
}
}
return errors;
};

export default validate;
[/cc]

数据对象 是[cc lang=”js”] {
clubName:xxx
members: [{
firstName: xxx,
hobbies: []
}]
}[/cc]

material UI width Redux-form

[cc lang=”js”]
// 选择 框
const renderSelectField = (
{ input, label, meta: { touched, error }, children, …custom },
) => (
input.onChange(value)}
children={children}
{…custom}
/>
);




[/cc]
[cc lang=”js”]
//输入框
const renderTextField = (
{ input, label, meta: { touched, error }, …custom },
) => (

);

[/cc]

[cc lang=”js”]
// Radio
const renderRadioGroup = ({ input, …rest }) => (
input.onChange(value)}
/>
);



[/cc]

[cc lang=”js”]
// 本地和服务器验证
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

export default (async function asyncValidate(values /*, dispatch */) {
await sleep(1000); // simulate server latency
if ([“foo@foo.com”, “bar@bar.com”].includes(values.email)) {
return { email: “Email already Exists” };
}
});

export default reduxForm({
form: ‘MaterialUiForm’, // a unique identifier for this form
validate,
asyncValidate,
})(MaterialUiForm);[/cc]

初始化 表单数据
[cc lang=”js”]InitializeFromStateForm = reduxForm({
form: ‘initializeFromState’, // a unique identifier for this form
})(InitializeFromStateForm);

// You have to connect() to any reducers that you wish to connect to yourself
InitializeFromStateForm = connect(
state => ({
initialValues: state.account.data, // pull initial values from account reducer
}),
{ load: loadAccount }, // bind account loading action creator
)(InitializeFromStateForm);[/cc]

values lifecycle 生命周期
life cycle

forwardRef 转发 功能

ref 将不会透传下去。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。 所以在HOC 内部 使用forwardRef,把ref 传递给 包裹组件

# 子组件 导出时 加了高阶组件包裹
class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton);
# 父组件
import FancyButton from './FancyButton';
const ref = React.createRef();
// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}/>; 

幸运的是, React.forwardRef API 明确地将 refs 转发到内部的 FancyButton 组件

# 高阶组件
function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // 注意 React.forwardRef 回调的第二个参数 “ref”。
  // 我们可以将其作为常规 prop 属性传递给 LogProps,例如 
    “forwardedRef”
  // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

react-admin UI

1. SimpleForm 设置默认值 defaultValue={}

[cc lang=”js”]const customInput = ({record}) => (input type=”text” />)[/cc]

2 组件隐藏或显示

[cc lang=”js”]
import React from ‘react’;
import { EmailField } from ‘react-admin’;

const ConditionalEmailField = ({ record, …rest }) =>
record && record.hasEmail
?
: null;

export default ConditionalEmailField;[/cc]

——————————————————————
[cc lang=”js”] [/cc]
3 post 一条记录里有个标签数组, 根据 tag_id 查询标签
http://myapi.com/tags?id=[1,23,4] 使用GET_MANY

[cc lang=”js”]
[/cc]
根据post id 查询 这个post 下的 comments ,使用GET_MANY_REFERENCE
comments?post_Id= 12 // 默认会把id 赋给 post_id

————————————–

4 在提交之前更改表单值
https://marmelab.com/react-admin/Actions.html#query-and-mutation-components
https://marmelab.com/react-admin/Actions.html#altering-the-form-values-before-submitting

——————————————————————
5 警告错误 : Warning: React does not recognize the `basePath` prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom
attribute, spell it as lowercase `basepath` instead. If you
accidentally passed it from a parent component, remove it from the DOM
element.
In order to get rid of the basePath error, just sanitize the props passed to the Material UI Grid Component:

[cc lang=”js”]const SanitizedGrid = ({basePath, …props}) => {
return (

);
};[/cc]
————————————————–

6 数据查询与提交

dataProvider 可以用第四个参数处理 side effect 如 notification
[cc lang=”js”]// in src/comments/ApproveButton.js
import {
– showNotification,
UPDATE,
withDataProvider,
} from ‘react-admin’;
-import { push } from ‘react-router-redux’;

class ApproveButton extends Component {
handleClick = () => {
const { dataProvider, dispatch, record } = this.props;
const updatedRecord = { …record, is_approved: true };
– dataProvider(UPDATE, ‘comments’, { id: record.id, data: updatedRecord })
– .then(() => {
– dispatch(showNotification(‘Comment approved’));
– dispatch(push(‘/comments’));
– })
– .catch((e) => {
– dispatch(showNotification(‘Error: comment not approved’, ‘warning’))
– });
– }
+ dataProvider(UPDATE, ‘comments’, { id: record.id, data: updatedRecord }, {
+ onSuccess: {
+ notification: { body: ‘Comment approved’, level: ‘info’ },
+ redirectTo: ‘/comments’,
+ },
+ onFailure: {
+ notification: { body: ‘Error: comment not approved’, level: ‘warning’ }
+ }
+ })

render() {
return ;
}
}

ApproveButton.propTypes = {
dataProvider: PropTypes.func.isRequired,
– dispatch: PropTypes.func.isRequired,
record: PropTypes.object,
};

export default withDataProvider(ApproveButton);[/cc]

————————————————
7 根据权限显示哪些按钮

[cc lang=”js”]const UserCreateToolbar = ({ permissions, …props }) =>


{permissions === ‘admin’ &&
}
;

export const UserCreate = ({ permissions, …props }) =>

}
defaultValue={{ role: ‘user’ }}
>

{permissions === ‘admin’ &&
}

;[/cc]

next.js 服务器 渲染

1 CSS / Sass / Less / Stylus files 
具体用法见下链接

2 静态文件服务(如图像)
在根目录下新建文件夹叫static。代码可以通过/static/来引入相关的静态资源
[cc lang=”js”]export default () => my image[/cc]

head 组件
[cc lang=”js”]import Head from ‘next/head’
export default () => (

Hello world!

)[/cc]
只有第二个[cc lang=”html”][/cc]才被渲染。
注意:在卸载组件时,的内容将被清除。请确保每个页面都在其定义了所需要的内容,而不是假设其他页面已经加过了

获取数据以及组件生命周期
页面初始 数据
当页面渲染时加载数据,我们使用了一个异步方法getInitialProps 返回json 对象,绑定到函数或类的静态方法,这样props 就能获得这个对象。

注意: 1. 初始页面加载 getInitialProps 是在服务端运行的,不要把 getInitialProps 用到 类库带到 客户端, 具体方法见 https://www.michnal.com/?p=794
2. 当用户 在客户端 通过 Link 或routing APIs 导航 才在客户端执行
3. getInitialProps 不能用在 子组件, 要用pages 里面根组件

getInitialProps入参对象的属性如下:
pathname – URL 的 path 部分
query – URL 的 query 部分,并被解析成对象
asPath – 显示在浏览器中的实际路径(包含查询部分),为String类型
req – HTTP 请求对象 (只有服务器端有)
res – HTTP 返回对象 (只有服务器端有)
jsonPageRes – 获取数据响应对象 (只有客户端有)
err – 渲染过程中的任何错误

路由

客户端路由行为与浏览器很相似:

1.组件获取
2.如果组件定义了getInitialProps,数据获取了。如果有错误情况将会渲染 _error.js。
3. 1和2都完成了,pushState执行,新组件被渲染。

Link
[cc lang=”js”]
const as = {
pathname: ‘/about/next’,
hash: ‘title-1’
}

// 可以是 对象 href ={{pathname: ‘/about’,query: { name: ‘next’ } }}
// 可以加as 属性 表示浏览器显示 url , href 表示page 路径下js 文件
here
to read more

[/cc]
注意:可以使用<Link prefetch>使链接和预加载在后台同时进行,来达到页面的最佳性能。

[cc lang=”js”]
import Router, { withRouter } from ‘next/router’
const handleClick = () => Router.push(href, as)

export default withRouter(({ router: { query } }) => (

About {query.name}

{query.name === ‘zeit’ ? (
Go to home page
) : (

)}

))[/cc]

路由命令式导航

Dynamic Routing
consider the following page pages/post/[pid].js:

[cc lang=”js”]
//pages/post/[pid].js:
import { useRouter } from ‘next/router’

const Post = () => {
const router = useRouter()
const { pid } = router.query

return

Post: {pid}

}

export default Post
///post/abc?foo=bar will have the query object: { foo: ‘bar’, pid: ‘abc’ }.
[/cc]
Any route like /post/1, /post/abc, etc will be matched by pages/post/[pid].js.

拦截器 popstate
你可能想监听popstate,在路由跳转前做一些动作。 比如,你可以操作 request 或强制 SSR 刷新
[cc lang=”js”]
import Router from ‘next/router’

Router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== “/” || as !== “/other”) {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}

return true // 返回 false,Router将不会执行popstate事件
});[/cc]

以上Router对象的 API 如下:

route – 当前路由的String类型
pathname – 不包含查询内容的当前路径,为String类型
query – 查询内容,被解析成Object类型. 默认为{}
asPath – 展现在浏览器上的实际路径,包含查询内容,为String类型
push(url, as=url) – 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 url
replace(url, as=url) – performs a replaceState call with the given url
beforePopState(cb=function) – 在路由器处理事件之前拦截.
push 和 replace 函数的第二个参数as,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。

路由事件
routeChangeStart(url) – 路由开始切换时触发
routeChangeComplete(url) – 完成路由切换时触发
routeChangeError(err, url) – 路由切换报错时触发
beforeHistoryChange(url) – 浏览器 history 模式开始切换时触发
hashChangeStart(url) – 开始切换 hash 值但是没有切换页面路由时触发
hashChangeComplete(url) – 完成切换 hash 值但是没有切换页面路由时触发

这里的url是指显示在浏览器中的 url。如果你用了Router.push(url, as)(或类似的方法),那浏览器中的 url 将会显示 as 的值。

[cc lang=”js”]const handleRouteChange = url => {
console.log(‘App is changing to: ‘, url)
}

Router.events.on(‘routeChangeStart’, handleRouteChange)

Router.events.off(‘routeChangeStart’, handleRouteChange) //off事件去取消监听

//如果路由加载被取消(比如快速连续双击链接)
Router.events.on(‘routeChangeError’, (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
})

[/cc]

浅层路由

浅层路由允许你改变 URL 但是不执行getInitialProps生命周期。你可以加载相同页面的 URL,得到更新后的路由属性pathname和query,并不失去 state 状态。
注意: 浅层路由只作用于相同 URL 的参数改变,
[cc lang=”js”]// Current URL is “/”
const href = ‘/?counter=10’
const as = href
Router.push(href, as, { shallow: true })
//在组件里查看this.props.router.query你将会看到更新的 URL。
[/cc]

你可以在componentdidupdate钩子函数中监听 URL 的变化。
[cc lang=”js”]componentDidUpdate(prevProps) {
const { pathname, query } = this.props.router
// verify props have changed to avoid an infinite loop
if (query.id !== prevProps.router.query.id) {
// fetch data based on the new query
}
}[/cc]

命令式 prefetch 的支持
[cc lang=”js”]componentDidMount() {
const { router } = this.props
router.prefetch(‘/dynamic’)
}[/cc]

自定义服务端路由

禁止文件路由
默认情况,Next将会把/pages下的所有文件匹配路由(如/pages/some-file.js 渲染为 site.com/some-file)
[cc lang=”js”]
// next.config.js
module.exports = {
useFileSystemPublicRoutes: false
}
// 注意useFileSystemPublicRoutes只禁止服务端的文件路由;但是客户端的还是禁止不了。
//可以通过 配置客户端路由不能跳转文件路由
import Router from ‘next/router’
Router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes! zhi
if (as !== ‘/’ || as !== ‘/other’) {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}

return true
})
[/cc]

动态前缀
有时你需要设置动态前缀,可以在请求时设置assetPrefix改变前缀

动态导入
你可以动态导入 JavaScript 模块(如 React 组件)。
动态导入相当于把代码分成各个块管理。Next.js 服务端动态导入功能,你可以做很多炫酷事情。
[cc lang=”js”]
//基础支持
import dynamic from ‘next/dynamic’

const DynamicComponent = dynamic(import(‘../components/hello’))

export default () =>


HOME PAGE is here!

[/cc]

自定义<App>
在page 页面组件的基础上增加一些内容, 比如 页面布局, 使用componentDidCatch自定义处理错误 和 注入额外的数据

[cc lang=”js”]
import App, {Container} from ‘next/app’
import React from ‘react’

export default class MyApp extends App {
static async getInitialProps ({ Component, router, ctx }) {
let pageProps = {}

if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}

return {pageProps}
}

render () {
const {Component, pageProps} = this.props
return


}
}[/cc]

自定义<Document>
Next.js会自动定义文档标记,比如,你从来不需要添加, 等。如果想自定义文档标记,你可以新建./pages/_document.js,然后扩展Document类:
[cc lang=”js”]// _document is only rendered on the server side and not on the client side
// Event handlers like onClick can’t be added to this file

// ./pages/_document.js
import Document, { Head, Main, NextScript } from ‘next/document’

export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { …initialProps }
}

render() {
return (







)
}
}[/cc]
注意:
外的 React 组件将不会渲染到浏览器中,所以那添加应用逻辑代码。如果你页面需要公共组件(菜单或工具栏),可以参照上面说的App组件代替。

自定义错误处理
404和500错误客户端和服务端都会通过error.js组件处理。如果你想改写它,则新建_error.js在文件夹中:

[cc lang=”js”]import React from ‘react’

export default class Error extends React.Component {
static getInitialProps({ res, err }) {
const statusCode = res ? res.statusCode : err ? err.statusCode : null;
return { statusCode }
}

render() {
return (

{this.props.statusCode
? `An error ${this.props.statusCode} occurred on server`
: ‘An error occurred on client’}

)
}
}[/cc]

渲染内置错误页面
如果你想渲染内置错误页面,你可以使用next/error:

自定义配置
如果你想自定义 Next.js 的高级配置,可以在根目录下新建next.config.js文件(与pages/ 和 package.json一起)

注意:next.config.js是一个 Node.js 模块,不是一个 JSON 文件,可以用于 Next 启动服务已经构建阶段,但是不作用于浏览器端。
1.设置自定义构建目录
2. 禁止 etag 生成 //你可以禁止 etag 生成根据你的缓存策略。如果没有配置,Next 将会生成 etags 到每个页面中。
3.配置 onDemandEntries //Next 暴露一些选项来给你控制服务器部署以及缓存页面:
4. 配置构建 ID
Next.js 使用构建时生成的常量来标识你的应用服务是哪个版本。在每台服务器上运行构建命令时,可能会导致多服务器部署出现问题。为了保持同一个构建 ID,可以配置generateBuildId函数:

自定义 webpack 配置

Next.js 支持 IE11 和所有的现代浏览器使用了@babel/preset-env。为了支持 IE11,Next.js 需要全局添加Promise的 polyfill。
[cc lang=”js”]module.exports = {
webpack: function (cfg) {
const originalEntry = cfg.entry
cfg.entry = async () => {
const entries = await originalEntry()

if (
entries[‘main.js’] &&
!entries[‘main.js’].includes(‘./client/polyfills.js’)
) {
entries[‘main.js’].unshift(‘./client/polyfills.js’)
}

return entries
}

return cfg
}
}[/cc]

多 zone 合并 项目
一个 zone 时一个单独的 Next.js 应用。如果你有很多 zone,你可以合并成一个应用。
例如,你如下有两个 zone:

https://docs.my-app.com 服务于路由 /docs/**
https://ui.my-app.com 服务于所有页面
有多 zone 应用技术支持,你可以将几个应用合并到一个,而且可以自定义 URL 路径,使你能同时单独开发各个应用。

AMP Support

React 服务端渲染render

ReactDOMServer.renderToString(element)

如果你在已有服务端渲染标记的节点上调用 ReactDOM.hydrate() 方法,React 将会保留该节点且只进行事件处理绑定,从而让你有一个非常高性能的首次加载体验

renderToStaticMarkup()

此方法与 renderToString 相似,但此方法不会在 React 内部创建的额外 DOM 属性,例如 data-reactroot。

ReactDOMServer.renderToNodeStream(element)

将一个 React 元素渲染成其初始 HTML。返回一个可输出 HTML 字符串的可读流。通过可读流输出的 HTML 完全等同于 ReactDOMServer.renderToString 返回的 HTML。达到 SEO 优化的目的

注意:
这个 API 仅允许在服务端使用。不允许在浏览器使用。
通过本方法返回的流会返回一个由 utf-8 编码的字节流。如果你需要另一种编码的流,请查看像 iconv-lite 这样的项目,它为转换文本提供了转换流。

ReactDOMServer.renderToStaticNodeStream(element)

类似 renderToStaticMarkup

ReactDOM.hydrate(element, container[, callback])

与 render() 相同,但它用于在 ReactDOMServer 渲染的容器中对 HTML 的内容进行 hydrate 操作。React 会尝试在已有标记上绑定事件监听器。

防止 XSS vulnerability

serialize 代替 JSON.stringify(initialData)

import serialize from 'serialize-javascript';

 window.__INITIAL_STATE__ = ${serialize(initialData)};

next.js 或 PWAs 只在服务需要运行的模块,但会打包到 client 里

1. Webpack can’t statically analyze what’s inside eval. So it won’t bundle the faker module.
[cc lang=”js”]const faker = eval(“require(‘faker’)”)[/cc]

2. Create a file called next.config.js in your Next.js app and add the webpack IgnorePlugin.

[cc lang=”js”]module.exports = {
webpack: function (config) {
config.plugins.push(
new require(‘webpack’).IgnorePlugin(/faker/)
)

return config
}
}[/cc]

3. USING THE “BROWSER” FIELD OF THE “PACKAGE.JSON”
You can also ask webpack to disable bundling faker module via adding a config in the main package.json file.
Add the following code in to your package.json file.
[cc lang=”js”]{

“browser”: {
“faker”: false
}

}[/cc]

create react app

This project was bootstrapped with Create React App.

Available Scripts

In the project directory, you can run:

npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.

The page will reload if you make edits.
You will also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!

If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

createContext

[cc lang=”js”]
//后代组件中的组件放在Consumer里面, 内部是一个函数, 这个函数接受一个对象作为参数,
参数是Provider里面提供的值. 然后直接使用就可以了
export default class List extends React.Component{

render() {
const { list } = this.props
return (

{
({ deleteTask }) => {
return list.map((item, index) => {
return (

  • { item }

)
})
}
}

);
}
}[/cc]

import React from 'react'
const { Consumer, Provider } = React.createContext();

 / /父(祖先)级组件中, 把要传递东西的后代组件包起来, 要传递的东西放进value里面
function rootComponent(){
  render() {
     return (
      <input type="text" />
      <button> Add </button>

     );
   }
}
//后代组件中的组件放在Consumer里面, 内部是一个函数, 这个函数接受一个对象作为参数,
参数是Provider里面提供的值. 然后直接使用就可以了
export default class List extends React.Component{

render() {
     const { list } = this.props
       return (
           {
           ({ deleteTask }) => {
                return list.map((item, index) => {
                      return (
                               <ul>
 	                                <li>{ item }
                                        <button>{deleteTask(index)}}&gt; delete </button> 
                                 </li>
                               </ul>
                             )
                    })
             }
           }
      );
  }
}

新的Context有一个明显实用好处,可以很清晰地让多个Context交叉使用,比如组件树上可以有两个Context Provider,而Consumer总是可以区分开哪个context是哪个。

{ context1 => (
            { context2 => {
             // 在这里可以通过代码选择从context1或者context2中获取数据
            return ...;
          }
}

)
}

context Api 16.3 以前用法

[cc lang=”js”]
class ThemeProvider extends React.Component {
state = {
theme: defaultTheme
}
static childContextTypes = {
theme: PropTypes.object
}
getChildContext() {
return this.state;
// return this.props.theme 返回 provider 组件属性的theme
}
}
//每当ThemeProvider被渲染的时候,它的getChildContext就会被调用,返回值就是它所提供的context
[/cc]

[cc lang=”js”]
const Banner = ({}, context) => {
return (

Welcome!

);
};

Banner.contextTypes = {
theme: PropTypes.object
}[/cc]

GraphQL React

import ApolloClient from 'apollo-boost';

const client = new ApolloClient({
  uri: 'https://48p1r2roz4.sse.codesandbox.io',
});

import { gql } from "apollo-boost";
// or you can use `import gql from 'graphql-tag';` instead

...

client
  .query({
    query: gql`
      {
        rates(currency: "USD") {
          currency
        }
      }
    `
  })
  .then(result => console.log(result));

Connect your client to React

用到ApolloProvider ,

!!! 如果用到react-router 一般会在包裹在 root route 组件外层

import { ApolloProvider } from '@apollo/react-hooks';

const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h2>My first Apollo app ?</h2>
    </div>
  </ApolloProvider>
);

Request data

在 添加ApolloProvider 之后 就可以用useQuery hook 请求数据

import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';

function ExchangeRates() {
  const { loading, error, data } = useQuery(gql`
    {
      rates(currency: "USD") {
        currency
        rate
      }
    }
  `);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return data.rates.map(({ currency, rate }) => (
    <div key={currency}>
      <p>
        {currency}: {rate}
      </p>
    </div>
  ));
}